2012-05-29 7 views
14

Ich frage mich, warum MailboxProcessor 's Standardstrategie der Behandlung von Ausnahmen ist nur still ignorieren sie. Zum Beispiel:MailboxProcessor und Ausnahmen

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 

und nichts passiert. Es gibt keinen fatalen Stopp der Programmausführung, oder es erscheint ein Meldungsfeld mit "Eine unbehandelte Ausnahme". Nichts.

Diese Situation wird schlimmer, wenn jemand PostAndReply Methode verwendet: ein garantierter Deadlock als Ergebnis.

Gründe für ein solches Verhalten?

Antwort

5

Ich denke, der Grund, warum die MailboxProcessor in F # keinen Mechanismus für die Behandlung von Ausnahmen enthält, ist, dass es nicht klar ist, was der beste Weg ist, dies zu tun. Beispielsweise möchten Sie möglicherweise ein globales Ereignis, das ausgelöst wird, wenn eine nicht behandelte Ausnahme auftritt, aber die Ausnahme beim nächsten Aufruf an Post oder PostAndReply erneut auslösen möchten.

Beide Optionen können basierend auf dem Standard MailboxProcessor implementiert werden, sodass es möglich ist, das gewünschte Verhalten hinzuzufügen. Beispielsweise zeigt das folgende Snippet HandlingMailbox, das einen globalen Ausnahmebehandler hinzufügt. Es hat die gleiche Schnittstelle wie normale MailboxProcessor (ich einige Methoden weggelassen), aber es fügt OnError Ereignis, das ausgelöst wird, wenn eine Ausnahme auftritt:

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 

, es zu benutzen, würden Sie den gleichen Code wie das, was Sie schreiben schrieb vor , aber Sie können jetzt Ausnahmen asynchron behandeln:

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

Ja, es kann natürlich implementiert werden. Ich verstehe nicht, warum * Standard * Verhalten so sprachlos ist. Es kann eine Exception erneut ausgeben, wenn sich beispielsweise keine Handler in 'MailboxProcessor.add_Error' befinden. Es ist schwierig, Async/Multithread-Code zu debuggen. Warum sollten wir diese Aufgabe noch schwerer machen? – qehgt

+2

Sie können argumentieren, dass es besser wäre, den Prozess (unbehandelte Ausnahme) herunterzufahren, wenn keine Handler angehängt wurden. Ich erinnere mich nicht an die Gründe. – Brian

+1

Ein Problem beim erneuten Auslösen von Ausnahmen besteht darin, dass Sie den Stack-Trace verlieren - zum Beispiel 'async {failwith" bad "}' gibt einen Stack-Trace, der tief in die F # -Bibliotheken zeigt, was lästiges Debugging sein kann Erneutes Auslösen von Ausnahmen in einem anderen Thread –

12

Es ein Error Ereignis auf dem MailboxProcessor ist.

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

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

Natürlich können Sie so etwas wie Tomas' Lösung tun, wenn Sie selbst feine Kontrolle ausüben wollen.

+1

Ja, ich weiß über dieses Mitglied "Fehler". Ich frage mich, warum Standard auf Error-Handler nichts tut. Es kann erneut ausgeben oder in stderr schreiben oder die Anwendung anhalten - alles ist besser als Ausnahmen ignorieren. – qehgt

+1

Doh! Ich war auf der Suche nach einem vorhandenen Ereignis auf dem Standard "MailboxProcessor", aber irgendwie habe ich das "Error" -Ereignis komplett verpasst ... –

+0

Beachten Sie, dass Sie die Ausnahme auch "erhöhen" können, anstatt es zu drucken, wenn Sie möchten, dass Ihr Programm abstürzt Ein 'MailboxProcessor' schlägt fehl, anstatt weiter in einem potenziell beschädigten Zustand zu laufen. – spiffytech