tags:

views:

212

answers:

1

I have a mailbox processor which receives a fixed number of messages:

let consumeThreeMessages = MailboxProcessor.Start(fun inbox ->
        async {
            let! msg1 = inbox.Receive()
            printfn "msg1: %s" msg1

            let! msg2 = inbox.Receive()
            printfn "msg2: %s" msg2

            let! msg3 = inbox.Receive()
            printfn "msg3: %s" msg3
        }
    )

consumeThreeMessages.Post("First message")
consumeThreeMessages.Post("Second message")
consumeThreeMessages.Post("Third message")

These messages should be handled in exactly the order sent. During my testing, it prints out exactly what it should:

First message
Second message
Third message

However, since message posting is asynchronous, it sounds like posting 3 messages rapidly could result in items being processed in any order. For example, I do not want to receive messages out of order and get something like this:

Second message // <-- oh noes!
First message
Third message 

Are messages guaranteed to be received and processed in the order sent? Or is it possible for messages to be received or processed out of order?

+6  A: 

The code in your consumeThreeMessages function will always execute in order, because of the way F#'s async workflows work.

The following code:

   async {
            let! msg1 = inbox.Receive()
            printfn "msg1: %s" msg1

            let! msg2 = inbox.Receive()
            printfn "msg2: %s" msg2

        }

Roughly translates to:

async.Bind(
    inbox.Receive(),
    (fun msg1 -> 
         printfn "msg1: %s" msg1
         async.Bind(
             inbox.Receive(),
             (fun msg2 -> printfn "msg2: %s" msg2)
         )
    )
)

When you look at the desugared form, it is clear that the code executes in serial. The 'async' part comes into play in the implementation of async.Bind, which will start the computation asynchronously and 'wake up' when it completes to finish the execution. This way you can take advantage of asynchronous hardware operations, and not waste time on OS threads waiting for IO operations.

That doesn't mean that you can't run into concurrency issues when using F#'s async workflows however. Imagine that you did the following:

let total = ref 0

let doTaskAsync() = 
    async { 
        for i = 0 to 1000 do 
            incr total
    } |> Async.Start()

// Start the task twice
doTaskAsync()
doTaskAsync()

The above code will have two asynchronous workflows modifying the same state at the same time.

So, to answer your question in brief: within the body of a single async block things will always execute in order. (That is, the next line after a let! or do! doesn't execute until the async operation completes.) However, if you share state between two async tasks, then all bets are off. In that case you will need to consider locking or using Concurrent Data Structures that come with CLR 4.0.

Chris Smith
+1, +answer: makes perfect sense :) You know, I wasn't 100% sure at first because, just as an experiment, I wanted to implement mailboxprocessors in C#, and I kept getting unwanted results with sequential calls to my GetMsg(Action<T>>) implementation. My nested calls to `GetMsg(x => GetMsg(y => GetMsg(z => ...>)))` work fine, but sequential calls `inbox.GetMsg(x => ...); inbox.GetMsg(x => ); inbox.GetMsg(x => ...)` get processed out of order. Instead of "there's probably something wrong with my code", my first thought was "hey,I wonder if F# is broken?" Thankfully no ;)
Juliet
I am glad we have F# gurus on SO. Where else would we turn?
ChaosPandion
But can the `Post`s be reordered?
Jon Harrop