代码之家  ›  专栏  ›  技术社区  ›  Juliet

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

  •  7
  • Juliet  · 技术社区  · 16 年前

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

    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")
    

    这些消息应该按照发送的顺序进行处理。在我的测试过程中,它准确地打印出它应该:

    First message
    Second message
    Third message
    

    但是,由于消息发布是异步的,所以听起来快速发布3条消息可能会导致以任何顺序处理项目。例如,我 希望接收不正常的消息并获得如下内容:

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

    是否保证在发送的订单中接收和处理消息?或者,是否有可能接收或处理的消息有问题?

    1 回复  |  直到 16 年前
        1
  •  8
  •   Nathan Shively-Sanders    16 年前

    你的代码 consumeThreeMessages 函数将始终按顺序执行,这是因为f的异步工作流的工作方式。

    以下代码:

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

    大致翻译为:

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

    当您查看被删减的表单时,很明显代码是串行执行的。“异步”部分在 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()
    

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

    因此,简单地回答您的问题:在一个异步块的主体中,事情总是按顺序执行。(也就是说,下一行接一句!还是这样!在异步操作完成之前不会执行。)但是,如果在两个异步任务之间共享状态,则所有匹配都将关闭。在这种情况下,您需要考虑锁定或使用与clr 4.0一起提供的并发数据结构。