There is a lot of scenarios that a functional code can introduce benefits. But asynchronous and parallel programming is one of those that fit with functional programming perfectly. I’m going use one example of this in particular. That is when we write asynchronous or parallel code, we need to consciously think about how we access the data.
What happens in these situations is that one thread tries to update the data. While another thread tries to access the same data. The technical term for this kind of scenario is data access synchronization. So in this post, I’m going to show a scenario like this and solve it without using FP. Then I’m going to solve the same problem with a slight tweak to see how functional programming make life easier.
Asynchronous And Parallel Programming and Immutability
One of the most important tenant of functional programming is immutability. For example in F#, we need to explicitly specify that we want the value to be mutable. Otherwise the our value is immutable by default. This is important because immutable object are inherently thread-safe. They guarantee correctness by not allowing mutation so we can say our result is deterministic and this helps us reason about the correctness of our code.
One draw back of immutability might be performance, but there are collections in .Net framework that facilitate this problem. Collections such as
ImmutableDictionary returns a new dictionary every time we add an item. But it does it efficiently because it reuse the same memory when it tries to construct a new dictionary. Immutability ease concurrency but why is that? Let’s see an example.
Take a look at the code above, this simple example flip the value of a flag multiple time and print it’s value. Here’s the expected value when executed sequentially.
So as we can see the parallel code that executed from two different thread has an incorrect sequence of true and false. The reason is that the first thread wakes up, read the flag, see that is false, but before it can do anything, thread B wakes up and reads the flag, which is also false! So both will print false. So everything that is mutable in this code excerpt is not going to be deterministic when we use multiple thread.
So we have to wrap both the variable that we use for looping and the flag inside a
Non Functional Asynchronous Code
Suppose we have a class with a property called Amount and we have a method called RefreshAmountAsync which mutates our property as you can see below.
Now the problem is if the Amount is accessed by multiple thread and mutated by multiple thread, the value of the amount is not as reliable as it should. We some kind of mechanism to synchronize the access to our shared data. We can use
lock in normal situations but here we can’t, lock can’t be used with
await keyword. So we need to use something like
SemaphoreSlim to do that. So we synchronize the data access and refactor the code to what you can see below.
In the code above, we create a static field for
SemaphoreSlim. It’s static because it needs to marshal the access to the resource across all instances. We also set the constructor value that tell the
SemaphoreSlim how many instances are allowed to access the data. Finally in
RefreshAmountAsync method we call
WaitAsync to prevent any other instance from entering past this line. But we go through all of this because there is flaw in our design.
Functional Asynchronous Code
The flaw that I’m talking about is how we used the property in
AccountInfo class. So instead of setting the value for Amount so we can get it later, Why don’t we return the value directly to the caller every time?
Here we return the value instead of mutating the Amount property. Now anyone who needs the value needs to called the method every time. It might not be as efficient as when we use the property but there are other mechanism to address that. But by doing this we achieved simplicity in our code. I understand that this is a contrived example, there is always a trade off that we have to consider. The code here is simple but if the call to
RefreshAmountAsync is expansive, we’re in trouble.
In this post I discussed how to synchronize the data between different thread. I also explained how introducing functional programming into our application can simplify our asynchronous code. These functional concepts are simple, yet powerful, which can solve problems that can become unnecessarily cumbersome.