保证邮件发送到邮箱处理器的邮件顺序

Jul*_*iet 7 concurrency f#

我有一个邮箱处理器,它接收固定数量的邮件:

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")
Run Code Online (Sandbox Code Playgroud)

应该按照发送的顺序处理这些消息.在我的测试过程中,它会准确打印出它应该是什么:

First message
Second message
Third message
Run Code Online (Sandbox Code Playgroud)

但是,由于邮件发布是异步的,因此听起来像快速发布3条消息可能会导致按任何顺序处理项目.例如,我不想不按顺序接收消息并得到类似这样的信息:

Second message // <-- oh noes!
First message
Third message 
Run Code Online (Sandbox Code Playgroud)

是否保证在发送的订单中接收和处理消息?或者是否可能无序接收或处理消息?

Chr*_*ith 8

consumeThreeMessages由于F#的异步工作流的工作方式,函数中的代码将始终按顺序执行.

以下代码:

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

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

        }
Run Code Online (Sandbox Code Playgroud)

大致翻译为:

async.Bind(
    inbox.Receive(),
    (fun msg1 -> 
         printfn "msg1: %s" msg1
         async.Bind(
             inbox.Receive(),
             (fun msg2 -> printfn "msg2: %s" msg2)
         )
    )
)
Run Code Online (Sandbox Code Playgroud)

当您查看desugared表单时,很明显代码是串行执行的."异步"部分在实现中发挥作用async.Bind,它将异步启动计算并在完成执行时"唤醒".这样您就可以利用异步硬件操作,而不是在等待IO操作的OS线程上浪费时间.

但这并不意味着在使用F#的异步工作流时不会遇到并发问题.想象一下,你做了以下事情:

let total = ref 0

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

// Start the task twice
doTaskAsync()
doTaskAsync()
Run Code Online (Sandbox Code Playgroud)

上面的代码将有两个异步工作流同时修改相同的状态.

所以,简要回答你的问题:在单个异步块的主体内,事物将始终按顺序执行.(也就是说,let!或do!之后的下一行在异步操作完成之前不会执行.)但是,如果你在两个异步任务之间共享状态,那么所有的赌注都会关闭.在这种情况下,您需要考虑锁定或使用CLR 4.0附带的并发数据结构.