简体   繁体   English

邮箱处理器和例外

[英]MailboxProcessor and exceptions

I wonder, why MailboxProcessor 's default strategy of handling exceptions is just silently ignore them. 我想知道,为什么MailboxProcessor处理异常的默认策略只是默默地忽略它们。 For example: 例如:

let counter =
    MailboxProcessor.Start(fun inbox ->
        let rec loop() =
            async { printfn "waiting for data..."
                    let! data = inbox.Receive()
                    failwith "fail" // simulate throwing of an exception
                    printfn "Got: %d" data
                    return! loop()
            }
        loop ())
()
counter.Post(42)
counter.Post(43)
counter.Post(44)
Async.Sleep 1000 |> Async.RunSynchronously

and nothing happens. 没有任何反应。 There is no fatal stop of the program execution, or message box with "An unhandled exception" arises. 程序执行没有致命的停止,或者出现带有“未处理的异常”的消息框。 Nothing. 没有。

This situation becomes worse if someone uses PostAndReply method: a guaranteed deadlock as the result. 如果有人使用PostAndReply方法,这种情况会变得更糟:结果是保证死锁。

Any reasons for such behavior? 这种行为有什么理由吗?

There is an Error event on the MailboxProcessor. MailboxProcessor上有一个Error事件。

http://msdn.microsoft.com/en-us/library/ee340481 http://msdn.microsoft.com/en-us/library/ee340481

counter.Error.Add(fun e -> printfn "%A" e)

Of course, you can do something like Tomas' solution if you want to exert fine control yourself. 当然,如果你想自己施加精细控制,你可以做一些类似Tomas的解决方案。

I think the reason why the MailboxProcessor in F# does not contain any mechanism for handling exceptions is that it is not clear what is the best way for doing that. 我认为F#中的MailboxProcessor不包含任何处理异常的机制的原因是,目前尚不清楚这样做的最佳方法是什么。 For example, you may want to have a global event that is triggered when an unhandled exception happens, but you may want to rethrow the exception on the next call to Post or PostAndReply . 例如,您可能希望在发生未处理的异常时触发全局事件,但您可能希望在下次调用PostPostAndReply重新抛出异常。

Both of the options can be implemented based on the standard MailboxProcessor , so it is possible to add the behaviour you want. 这两个选项都可以基于标准的MailboxProcessor ,因此可以添加所需的行为。 For example, the following snippet shows HandlingMailbox that adds a global exception handler. 例如,以下代码段显示了添加全局异常处理程序的HandlingMailbox It has the same interface as normal MailboxProcessor (I omitted some methods), but it adds OnError event that is triggered when an exception happens: 它具有与普通MailboxProcessor相同的接口(我省略了一些方法),但它添加了在发生异常时触发的OnError事件:

type HandlingMailbox<'T> private(f:HandlingMailbox<'T> -> Async<unit>) as self =
  let event = Event<_>()
  let inbox = new MailboxProcessor<_>(fun inbox -> async {
    try 
      return! f self
    with e ->
      event.Trigger(e) })
  member x.OnError = event.Publish
  member x.Start() = inbox.Start()
  member x.Receive() = inbox.Receive()
  member x.Post(v:'T) = inbox.Post(v)
  static member Start(f) =
    let mbox = new HandlingMailbox<_>(f)
    mbox.Start()
    mbox

To use it, you would write the same code as what you wrote before, but you can now handle exceptions asynchronously: 要使用它,您将编写与之前编写的代码相同的代码,但您现在可以异步处理异常:

let counter = HandlingMailbox<_>.Start(fun inbox -> async {
  while true do 
    printfn "waiting for data..." 
    let! data = inbox.Receive() 
    failwith "fail" })

counter.OnError.Add(printfn "Exception: %A")
counter.Post(42) 

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

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