Почтовый ящик и исключения

Интересно, почему 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: гарантированный тупик в качестве результата.

Любые причины такого поведения?

Ответы

Ответ 1

Я думаю, причина, по которой MailboxProcessor в F # не содержит какого-либо механизма обработки исключений, заключается в том, что неясно, как лучше всего это сделать. Например, вы можете захотеть иметь глобальное событие, которое запускается, когда происходит необработанное исключение, но вы можете захотеть повторить исключение при следующем вызове Post или PostAndReply.

Оба варианта могут быть реализованы на основе стандартного 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) 

Ответ 2

На MailboxProcessor есть событие Error.

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

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

Конечно, вы можете сделать что-то наподобие решения Tomas, если вы хотите сами себя контролировать.