Подождите на процессоре почтового ящика

Можно ли ждать процессора почтовых ящиков, следующий код работает в интерактивном 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)

person Developer11    schedule 03.04.2018    source источник


Ответы (2)


Это невозможно именно в этом сценарии, но вы можете определить тип своего сообщения, включив в него AsyncReplyChannel‹'t>, что позволяет использовать MailboxProcessor.PostAndReply вместо Post. Таким образом, вызывающий код может (синхронно или асинхронно) ожидать ответного значения или, по крайней мере, индикации того, что обработка завершена.

Ваш измененный исходный код может выглядеть так:

[<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 — это немного сложная тема, поэтому убедитесь, что вы всегда используете тайм-ауты, иначе в случае ошибок в вашем коде или исключений, прерывающих цикл обработки сообщений, ваш код зависнет навсегда. Плохо в тестах, еще хуже в продакшене.

person Honza Brestan    schedule 03.04.2018

Вы должны использовать PostAndAsyncReply или PostAndReply (версия с блокировкой)

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"
person Szer    schedule 03.04.2018