Fsharp中的异步行为

时间:2012-02-12 21:07:58

标签: asynchronous f#

运行以下代码时

open System
open Microsoft.FSharp.Control

type Message(id, contents) =
    static let mutable count = 0
    member this.ID = id
    member this.Contents = contents
    static member CreateMessage(contents) =
        count <- count + 1
        Message(count, contents)

let mailbox = new MailboxProcessor<Message>(fun inbox ->
    let rec loop count =
        async { printfn "Message count = %d. Waiting for next message." count
                let! msg = inbox.Receive()
                printfn "Message received. ID: %d Contents: %s" msg.ID msg.Contents
                return! loop( count + 1) }
    loop 0)

mailbox.Start()

mailbox.Post(Message.CreateMessage("ABC"))
mailbox.Post(Message.CreateMessage("XYZ"))

//System.Threading.Thread.Sleep(500)
Console.WriteLine("Press any key...")
Console.ReadLine() |> ignore

我得到以下结果

> Press any key...
Message count = 0. Waiting for next message.
Message received. ID: 1 Contents: ABC
Message count = 1. Waiting for next message.
Message received. ID: 2 Contents: XYZ
Message count = 2. Waiting for next message.

我希望msg按任意键在第一条消息之后出现...

如果我包括睡眠,它确实会发生。

所以我的问题是:

这是一个带走的课程,在使用异步方法时,您不能指望任何特定的顺序。又名,异步中的代码可以从没有特定优先级开始

2 个答案:

答案 0 :(得分:5)

来自mailboxProcessor的文档(此代码示例所在的文档)

  

Post

     

以异步方式将邮件发送到MailboxProcessor的邮件队列。

注意 - Post没有保证处理 - 这就是异步背后的整个想法。如果你需要等待计算完成,你需要使用PostAndReply - 虽然此时你会失去多线程的一些好处。

MailboxProcessor将始终按顺序处理邮件,但除非您等待,否则邮件将无法完成处理

答案 1 :(得分:5)

正如John所解释的那样,Post方法只是将邮件发布到邮箱以供以后处理。可以在发送方线程上发生其他事情之前处理该消息,但它可能不会 - 这仅仅取决于线程调度,并且无法控制该消息。

如果要将邮件发送到邮箱并等待结果,则需要使用PostAndReply方法(或者,更好地使用异步工作流程中的PostAndAsyncReply来避免阻止)。这是一个例子:

/// The message carries AsyncReplyChannel<unit>, which is used to
/// notify the caller that the message was processed.
type Message = 
  | Message of int * string * AsyncReplyChannel<unit> 

let mailbox = new MailboxProcessor<Message>(fun inbox -> 
    let rec loop count = 
        async { printfn "Message count = %d. Waiting for next message." count 
                // Receive & process the message
                let! (Message(id, contents, repl)) = inbox.Receive() 
                printfn "Message received. ID: %d Contents: %s" msg.ID msg.Contents 
                // Notify the caller that processing has finished
                repl.Reply()
                return! loop( count + 1) } 
    loop 0) 

mailbox.Start() 

// Send message and wait for reply using 'PostAndReply' method
mailbox.PostAndReply(fun chnl -> Message(0, "ABC", chnl))
mailbox.PostAndReply(fun chnl -> Message(0, "XYZ", chnl))