简体   繁体   English

等待邮箱处理器

[英]Wait on the mailboxprocessor

Is it possible to wait on the mailboxprocessor, following code works in F# interactive but is there a way to wait on it in an application or a unit test? 是否可以等待邮箱处理器,以下代码在F#交互中有效,但是有没有办法在应用程序或单元测试中等待它?

[<TestMethod>]
member this.TestMailboxProcessor() =
    let mailboxProcessor = MailboxProcessor<string>.Start(fun inbox ->
        async {
            while true do
            let! msg = inbox.Receive()
            printfn "agent got message %s" msg // too late, UnitTest exits
        }
    )

    mailboxProcessor.Post "ping"
    Console.WriteLine "message posted" // I see this in the console
    Assert.IsTrue(true)

It's not possible in exactly this scenario, but you can define your message type to include an AsyncReplyChannel<'t> , which then allows you to use MailboxProcessor.PostAndReply instead of Post. 在这种情况下,这是不可能的,但是您可以定义消息类型以包括AsyncReplyChannel <'t> ,然后允许您使用MailboxProcessor.PostAndReply而不是Post。 This way the calling code can (either synchronously or asynchronously) wait for a response value, or at least an indication that the processing is done. 这样,调用代码可以(同步或异步)等待响应值,或者至少等待处理已完成的指示。

Your modified source code may look like this: 您修改后的源代码可能如下所示:

[<TestMethod>]
member this.TestMailboxProcessor() =
    let mailboxProcessor =
        MailboxProcessor<string * AsyncReplyChannel<unit>>.Start(fun inbox ->
            async {
                while true do
                let! msg, replyChannel = inbox.Receive()
                printfn "agent got message %s" msg
                (* 
                  Reply takes a value of the generic param of
                  AsyncReplyChannel<'t>, in this case just a unit
                *)
                replyChannel.Reply()
            }
        )

    (*
      You can't create an AsyncReplyChannel<'t> value, but this does it for you.
      Also always, always use timeouts when awaiting message replies. 
    *)
    mailboxProcessor.PostAndReply(
        (fun replyChannel -> "ping", replyChannel),
        timeout = 1000)
    (* This gets printed only after the message has been posted and processed *)
    Console.WriteLine "message posted"
    Assert.IsTrue(true)

MailboxProcessors are a bit tricky topic though, so make sure you always use timeouts, otherwise in case of errors in your code, or exceptions killing the message loop, your code would hang forever. 但是,MailboxProcessor的主题有点棘手,因此请确保始终使用超时,否则,如果代码中出现错误或导致消息循环中断的异常,则代码将永远挂起。 Not good in tests, even worse in production. 测试不佳,甚至生产不佳。

You should use PostAndAsyncReply or PostAndReply (blocking version) 您应该使用PostAndAsyncReplyPostAndReply (阻止版本)

let replyAgent = MailboxProcessor.Start(fun inbox ->
    let rec loop() = 
        async {
            let! (replyChannel: AsyncReplyChannel<_>), msg = inbox.Receive()
            replyChannel.Reply (sprintf "replied for message: %A" msg)
            return! loop()
        }
    loop() )

let reply = replyAgent.PostAndReply(fun replCh -> replCh, "Hi")
printfn "%s" reply //prints "replied for message: Hi"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM