Fsharp中的异步行为

nic*_*las 1 f# asynchronous

运行以下代码时

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

我得到以下结果

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

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

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

所以我的问题是:

是拿走的教训,采用异步方法时,你不能指望任何特定顺序的这一点.又是,async中代码可以从没有特定的优先级开始

Joh*_*mer 5

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

Post

以异步方式将消息发送到MailboxProcessor的消息队列.

注意 - Post没有关于处理的保证 - 这是异步背后的整个想法.如果您需要等待计算完成,则需要使用PostAndReply- 尽管此时您将失去多线程的一些好处.

MailboxProcessor公司将始终按顺序处理消息,但除非你等着吧,消息将无法完成的处理


Tom*_*cek 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))
Run Code Online (Sandbox Code Playgroud)