【问题标题】:Async.Catch doesnt work on OperationCanceledExceptionsAsync.Catch 对 OperationCanceledException 不起作用
【发布时间】:2013-08-16 13:53:10
【问题描述】:

我使用 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 块吗?

【问题讨论】:

    标签: exception-handling f# async-workflow


    【解决方案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"
    

    【讨论】:

    • 感谢您的解释,有道理。您还回答了我的另一个问题,即“取消工作流时,这也会取消所有子计算”。我正要问这个问题,因为 MSDN 文档没有说明当父级被取消时子级计算会发生什么。
    猜你喜欢
    • 1970-01-01
    • 2015-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    • 2015-03-05
    • 2011-03-30
    • 2018-06-14
    相关资源
    最近更新 更多