2012-05-29 11 views
14

Me pregunto por qué la estrategia predeterminada de MailboxProcessor para manejar excepciones es solo ignorarlas silenciosamente. Por ejemplo:MailboxProcessor y excepciones

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 

y no ocurre nada. No hay una parada fatal de la ejecución del programa, o aparece un cuadro de mensaje con "Una excepción no controlada". Nada.

Esta situación empeora si alguien usa el método PostAndReply: como resultado, se produce un punto muerto garantizado.

¿Algún motivo para tal comportamiento?

Respuesta

5

Creo que la razón por la cual MailboxProcessor en F # no contiene ningún mecanismo para manejar excepciones es que no está claro cuál es la mejor manera de hacerlo. Por ejemplo, es posible que desee tener un evento global que se active cuando ocurra una excepción no controlada, pero es posible que desee volver a lanzar la excepción en la siguiente llamada al Post o al PostAndReply.

Ambas opciones se pueden implementar según el estándar MailboxProcessor, por lo que es posible agregar el comportamiento que desee. Por ejemplo, el siguiente fragmento muestra HandlingMailbox que agrega un manejador de excepción global. Tiene la misma interfaz que la normalidad MailboxProcessor (I omitido algunos métodos), pero añade OnError evento que se activa cuando una excepción que sucede:

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 

Para usarlo, podría escribir el mismo código que lo que escribió antes , pero ahora se puede controlar las excepciones de forma asíncrona:

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í, se puede implementar, por supuesto. No entiendo por qué el comportamiento * predeterminado * es tan sin palabras. Puede volver a lanzar la excepción en caso de que no haya controladores en 'MailboxProcessor.add_Error', por ejemplo. Es difícil depurar el código Async/multiproceso. ¿Por qué deberíamos hacer esta tarea aún más difícil? – qehgt

+2

Puede argumentar que sería mejor anular el proceso (excepción no controlada) si no se han adjuntado controladores. No recuerdo el razonamiento. – Brian

+1

Un problema con las excepciones de reintroducción es que pierde el seguimiento de la pila; por ejemplo, 'async {failwith" bad "}' da un rastro de pila que apunta profundo en las bibliotecas F #, lo que puede ser una depuración molesta; aparentemente, esto es una limitación .NET en reintroducción de excepciones en un hilo diferente –

12

Hay un evento en el Error MailboxProcessor.

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

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

Por supuesto, usted puede hacer algo como solución Tomas' si quiere ejercer un control fino sí mismo.

+1

Sí, sé de este miembro 'Error'. Me pregunto por qué el controlador predeterminado de Error no hace nada. Puede volver a lanzar o escribir en stderr o detener la aplicación; cualquier cosa es mejor que simplemente ignorar silenciosamente las excepciones. – qehgt

+1

¡Doh! Estaba buscando un evento existente en 'MailboxProcessor' estándar, pero de alguna manera me perdí por completo el evento' Error' ... –

+0

Tenga en cuenta que también puede 'raise' la excepción en lugar de imprimirlo, si desea que su programa se bloquee cuando un 'MailboxProcessor' falla en lugar de seguir ejecutándose en un estado potencialmente roto. – spiffytech