2013-08-16 10 views
6

ich Async.Catch Ausnahmen von Asynchron-Workflows geworfen zu handhaben:Async.Catch funktioniert nicht auf OperationCanceledExceptions

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

Heute habe ich bemerkt, dass OperationCanceledExceptions nicht von Async.Catch behandelt. Anstatt eine Auswahl aus Async.Catch zu bekommen, sprudelt die Exception weiter, bis sie mich trifft. Ich erwartete, der folgende Test rot zu sein, aber es ist grün:

[<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> 

einige Ausnahmen mit Async.Catch Auswertung + Auswahl + Matching und einige andere mit try/catch-Blöcke scheint nicht richtig ... es aussehen würde, wie das folgende, was viel zu kompliziert ist. Außerdem frage ich mich, was verwendet Async.Catch hat, da ich sowieso einen try/catch-Block verwenden ...:

[<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 

Was ist der beste Weg, um Ausnahmen von Asynchron-Workflows zu behandeln? Sollte ich nur Async.Catch ablegen und try/catch-Blöcke überall verwenden?

Antwort

6

Die Stornierung ist eine besondere Ausnahme in asynchronen Berechnungen. Wenn ein Workflow abgebrochen wird, werden auch alle untergeordneten Berechnungen abgebrochen (das Löschungs-Token wird geteilt). Also, wenn Sie die Löschung als normale Ausnahme behandeln könnten, könnte es immer noch einige andere Teile Ihrer Berechnung abbrechen (und es wäre schwierig darüber nachzudenken, was vor sich geht).

Sie können jedoch ein primitives Element schreiben, das einen Workflow startet (und es vom übergeordneten Workflow trennt) und dann die Stornierung in diesem Unterworkflow behandelt.

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)) 

Die Verwendung ist ähnlich wie Async.Catch, aber Sie haben die Stornierung Token StartCatchCancellation eher passieren, als es an die Haupt RunSynchronously vorbei (da der Workflow separat gestartet wird):

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" 
+0

Vielen Dank für die Erklärung, macht Sinn. Sie haben auch eine andere Frage beantwortet: "Wenn ein Workflow abgebrochen wird, werden auch alle untergeordneten Berechnungen abgebrochen". Ich wollte dies fragen, da die MSDN-Dokumente nicht sagen, was mit den Kindberechnungen passiert, wenn ihre Eltern gelöscht werden. – stmax

Verwandte Themen