2012-05-29 10 views
14

Mi chiedo, perché la strategia predefinita di gestione di MailboxProcessor di gestire le eccezioni è semplicemente ignorare in modo invisibile. Ad esempio:MailboxProcessor ed eccezioni

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 

e non succede nulla. Non vi è alcun arresto irreversibile nell'esecuzione del programma, oppure viene visualizzata una finestra di messaggio con "Un'eccezione non gestita". Niente.

Questa situazione peggiora se qualcuno utilizza il metodo PostAndReply: un deadlock garantito come risultato.

Eventuali motivi per tale comportamento?

risposta

5

Penso che il motivo per cui lo MailboxProcessor in F # non contenga alcun meccanismo per la gestione delle eccezioni è che non è chiaro quale sia il modo migliore per farlo. Ad esempio, potrebbe essere necessario avere un evento globale che viene attivato quando si verifica un'eccezione non gestita, ma è possibile che si desideri rilanciare l'eccezione alla prossima chiamata a Post o PostAndReply.

Entrambe le opzioni possono essere implementate in base allo standard MailboxProcessor, quindi è possibile aggiungere il comportamento desiderato. Ad esempio, il seguente snippet mostra HandlingMailbox che aggiunge un gestore di eccezioni globale. Ha la stessa interfaccia come normale MailboxProcessor (ho omesso alcuni metodi), ma aggiunge OnError evento che viene attivato quando un'eccezione accade:

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 

Per usarlo, si potrebbe scrivere lo stesso codice come quello che hai scritto prima , ma ora è possibile gestire le eccezioni in modo asincrono:

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

Sì, può essere implementato, ovviamente. Non capisco, perché * il comportamento predefinito * è così senza parole. Può rilanciare l'eccezione nel caso in cui non ci siano gestori in 'MailboxProcessor.add_Error', ad esempio. È difficile eseguire il debug del codice Async/multithread. Perché dovremmo rendere questo compito ancora più difficile? – qehgt

+2

È possibile sostenere che sarebbe preferibile annullare il processo (eccezione non gestita) se non sono stati associati gestori. Non ricordo la logica. – Brian

+1

Un problema con rilanciando le eccezioni è che si perde la traccia dello stack - ad esempio 'async {failwith" bad "}' dà una traccia stack che punta in profondità nelle librerie F #, che può essere fastidioso debugging - apparentemente questa è una limitazione .NET su eccezioni di rilancio su un thread diverso –

12

C'è un evento Error sul MailboxProcessor.

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

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

Naturalmente, si può fare qualcosa di simile soluzione Tomas' se si vuole esercitare un controllo accurato da soli.

+1

Sì, so di questo membro 'Errore'. Mi chiedo perché il default su Error handler non faccia nulla. Può eseguire il re-throw, o scrivere su stderr o arrestare l'applicazione - tutto è meglio che ignorare silenziosamente le eccezioni. – qehgt

+1

Doh! Stavo cercando un evento esistente su standard MailboxProcessor, ma in qualche modo ho completamente perso l'evento 'Error' ... –

+0

Nota che puoi anche" aumentare "l'eccezione invece di stamparla, se vuoi che il tuo programma si blocchi quando un 'MailboxProcessor' fallisce invece di continuare a girare in uno stato potenzialmente rotto. – spiffytech