Async.Catch не работает с OperationCanceledExceptions

Я использую Async.Catch для обработки исключений, создаваемых асинхронными рабочими процессами:

work
|> Async.Catch
|> Async.RunSynchronously
|> fun x -> match x with
            | Choice1Of2 _ -> () // success
            | Choice2Of2 ex -> // failure, handle exception

Сегодня я заметил, что OperationCanceledExceptions не обрабатываются Async.Catch. Вместо того, чтобы получить выбор от Async.Catch, исключение продолжает всплывать, пока не доходит до меня. Я ожидал, что следующий тест будет красным, но он зеленый:

  [<Test>]
  let ``Async.Catch doesnt work on OperationCancelledExceptions``() =
    use cancellationTokenSource = new System.Threading.CancellationTokenSource(1000)

    let work = async {
      while true do
        do! Async.Sleep 100
    }

    (fun () -> work
               |> Async.Catch
               |> fun x -> Async.RunSynchronously (x, cancellationToken=cancellationTokenSource.Token)
               |> ignore)
    |> should throw typeof<System.OperationCanceledException>

Оценка некоторых исключений с помощью Async.Catch + Choices + сопоставление и некоторых других с использованием блоков try/catch не кажется правильной... это будет выглядеть следующим образом, что слишком сложно. Кроме того, мне интересно, какая польза от Async.Catch, так как я все равно должен использовать блок try/catch...:

  [<Test>]
  let ``evaluating exceptions of async workflows``() =
    use cancellationTokenSource = new System.Threading.CancellationTokenSource(1000)

    let work = async {
      while true do
        do! Async.Sleep 100
    }

    try
      work
      |> Async.Catch
      |> fun x -> Async.RunSynchronously (x, cancellationToken=cancellationTokenSource.Token)
      |> fun x -> match x with
                  | Choice1Of2 result -> () // success, process result
                  | Choice2Of2 ex -> () // failure, handle exception
    with ex -> () // another failure, handle exception here too

Как лучше всего обрабатывать исключения асинхронных рабочих процессов? Должен ли я просто сбросить Async.Catch и везде использовать блоки try/catch?


person stmax    schedule 16.08.2013    source источник


Ответы (1)


Отмена — это особый вид исключений в асинхронных вычислениях. Когда рабочий процесс отменяется, это также отменяет все дочерние вычисления (токен отмены является общим). Таким образом, если бы вы могли обрабатывать отмену как обычное исключение, это все равно могло бы отменить некоторые другие части ваших вычислений (и было бы трудно рассуждать о том, что происходит).

Однако вы можете написать примитив, который запускает рабочий процесс (и отделяет его от родительского рабочего процесса), а затем обрабатывает отмену в этом подчиненном рабочем процессе.

type Async = 
  static member StartCatchCancellation(work, ?cancellationToken) = 
    Async.FromContinuations(fun (cont, econt, _) ->
      // When the child is cancelled, report OperationCancelled
      // as an ordinary exception to "error continuation" rather
      // than using "cancellation continuation"
      let ccont e = econt e
      // Start the workflow using a provided cancellation token
      Async.StartWithContinuations( work, cont, econt, ccont, 
                                    ?cancellationToken=cancellationToken) )

Использование аналогично Async.Catch, но вы должны передать токен отмены в StartCatchCancellation, а не в основной RunSynchronously (поскольку рабочий процесс запускается отдельно):

let work = 
  async { while true do
            do! Async.Sleep 100 }

let ct = new System.Threading.CancellationTokenSource(10000)
Async.StartCatchCancellation(work, ct.Token) 
|> Async.Catch
|> Async.RunSynchronously 
|> printfn "%A"
person Tomas Petricek    schedule 16.08.2013
comment
Спасибо за объяснение, понятно. Вы также ответили на другой вопрос, который у меня был: Когда рабочий процесс отменяется, это также отменяет все дочерние вычисления. Я собирался спросить об этом, поскольку документы MSDN не говорят, что происходит с дочерними вычислениями, когда их родитель отменяется. - person stmax; 16.08.2013