F#’s MailboxProcessor
One of the things I recently came across in F# is the MailboxProcessor. It struck my interest and I wanted to take some time to blog about it here.
The Problem With Mutable State
Almost all of our programs and applications contain some type of state. In a multi threaded environment, storing state can lead to problems.
For a simple example, lets count from 0 to 1000 in parallel.
We initialize a counter (line 1), create an async operation to update that counter (lines 2-5), then replicate it 1000 times (line 8), run in parallel (line 9), wait for all to finish (line 10) then print the value (line 13).
Care to guess what the output is? If you’re familiar with concurrency issues the answer is ???. I ran this code three times and got these values (894, 916, 880).
Why is that? We have 1000 replications of updateCounterAsync. Since the counter update isn’t protected, some are reading counter while others are writing counter. This leads to some of the updates get overridden.
That’s a problem, here’s two ways to correct it.
Using Locks
One way to fix this is to introduce a lock. Which is what I naturally gravitate towards.
Locks work by blocking access for anyone else, that ensures that only one thread at a time is updated counter.
Now that the counter
update is locked, running this always outputs 1000, which is the answer that we’re looking for.
Locking is great, but since this is an article about MailboxProcessor, lets demonstrate that.
Using MailboxProcessor
The MailboxProcessor handles concurrency a different way. It is a dedicated message queue running on its own logical thread of control1.
MailboxProcessor deals with messages. Message that are sent to it are queued until MailboxProcessor is ready to process them. This guarantees messages are processed in order and won’t overwrite each other.
Here is what a counter would look like using the MailboxProcessor.
We have two different messages, Incr
and Fetch
. The MailboxProcessor runs a function recursively, the first thing it does when it enters the function is receive a message.
If it receives an Incr
message, it adds one to n
and calls itself again. If it receives a Fetch
message, it sends back the current value of the counter.
We want to hide the internal details, so interaction with our MailboxProcessor is controlled by our two methods, Incr()
and Fetch()
.
Notice there’s a timeout on Fetch()
, so it’s not guaranteed to always return the value. Which is a complexity and a reason not to use MailboxProcessor.
Since we’re dealing with messages now, we also need some slight updates to our calling code.
Final Thoughts
Which technique is better? Personally, I would choose the locking approach. That’s mainly from my experience with other languages. This is the first time exploring MailboxProcessor, so maybe my mind will change in the future if I continue to use it.