2012-05-29 24 views
14

我不知道,为什么MailboxProcessor的默认处理异常的策略只是他们默默地忽略了。例如:邮箱处理器和例外

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 

并没有任何反应。没有致命的停止程序执行,或者出现带有“未处理的异常”的消息框。没有。

如果有人使用PostAndReply方法,那么这种情况会变得更糟:结果是保证了死锁。

这种行为的任何原因?

回答

5

我认为F#中MailboxProcessor不包含任何处理异常的机制的原因是,目前尚不清楚什么是最好的方式。例如,您可能希望在发生未处理的异常时触发全局事件,但您可能希望在下次调用PostPostAndReply时重新抛出异常。

这两个选项都可以基于标准MailboxProcessor实现,因此可以添加所需的行为。例如,以下代码片段显示HandlingMailbox,它添加了一个全局异常处理程序。它具有相同的接口,正常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 

要使用它,你会写相同的代码,你以前写过什么,但是现在可以异步处理异常:

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) 
+1

是的,当然可以实施。我不明白,为什么* default *行为如此无语。例如,如果在'MailboxProcessor.add_Error'中没有处理程序,它可以重新抛出异常。 调试Async/multithreaded代码很困难。为什么我们应该更加努力完成这项任务? – qehgt

+2

您可以争辩说,如果没有附加处理程序,最好取消进程(未处理的异常)。我不记得理由。 – Brian

+1

重新抛出异常的一个问题是,你失去了堆栈跟踪 - 例如'async {failwith“bad”}'给出了一个堆栈跟踪深入F#libs,这可能是恼人的调试 - 显然这是一个.NET限制在不同的线程上重新抛出异常 –

12

MailboxProcessor上有一个Error事件。

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

​​

当然,如果你想自己发挥很好的控制,你可以这样做托马斯的解决方案。

+1

是的,我知道这个成员'错误'。我想知道为什么默认的错误处理程序没有做任何事情。它可以重新抛出,或写入stderr,或停止应用程序 - 任何事情都比只是默默地忽略异常要好。 – qehgt

+1

Doh!我正在寻找标准'MailboxProcessor'上的现有事件,但不知何故,我完全错过了'Error'事件... –

+0

请注意,如果您希望程序崩溃时您还可以'提升'异常而不是打印它一个'MailboxProcessor'失败了,而不是继续在一个可能被破坏的状态下运行。 – spiffytech