在 FromContinuations 中使用取消延续

Using the cancellation continuation in FromContinuations

我试图通过我用 Async.FromContinuations 创建的 Async<'T> 来理解 async 工作流,但看不到如何使用取消延续。我正在尝试这个:

open System

let asyncComputation divisor =
    Async.FromContinuations
        (fun (success, except, cancel) ->
            try
                printfn "Going to sleep..."
                Threading.Thread.Sleep 3000
                printfn "...waking up"
                1 / divisor |> ignore
                printfn "Calling success continuation..."
                success ()
            with
            | :? OperationCanceledException as e ->
                printfn "Calling cancellation continuation..."
                cancel e
            | e ->
                printfn "Calling exception continuation..."
                except e)

[<EntryPoint>]
let main argv =
    use tokenSource = new Threading.CancellationTokenSource ()
    Async.Start (asyncComputation (int argv.[0]), tokenSource.Token)
    Console.ReadLine () |> ignore
    tokenSource.Cancel ()

运行 参数 1 导致唤醒后调用成功延续; 运行 参数 0 导致在唤醒后调用异常延续,产生预期的异常输出。到目前为止,一切都很好。但是当我在 3 秒睡眠期间通过按下 Enter 键取消(使用任一参数)时,它显然取消了异步计算而不调用取消继续。那么取消continuation在FromContinuations中应该如何使用,又应该如何触发取消从而调用取消continuation呢?

如果您在异步计算中使用CancellationToken,F# async workflows 会自动传播它,以便您可以在任何地方访问它,但您仍然需要显式检查是否已触发取消并抛出异常你自己。这不会在您的同步用户代码中的任何地方“自动”发生。

这意味着问题有两个部分。 1) 如何访问取消令牌以及 2) 如何检查它是否已被取消。

第二部分有点棘手,因为你需要使用 Async.CancellationToken,但是这个 returns Async<CancellationToken> 所以你必须在异步块中调用它,在FromContinuations 代码。第二部分只是在代码中某处调用 tok.ThrowIfCancellationRequested() 的问题。

在执行除法之前进行以下取消检查:

let asyncComputation divisor = async {
    let! tok = Async.CancellationToken
    return! Async.FromContinuations
        (fun (success, except, cancel) ->
            try
                printfn "Going to sleep..."
                Threading.Thread.Sleep 3000
                printfn "...waking up"
                tok.ThrowIfCancellationRequested()
                1 / divisor |> ignore
                printfn "Calling success continuation..."
                success ()
            with
            | :? OperationCanceledException as e ->
                printfn "Calling cancellation continuation..."
                cancel e
            | e ->
                printfn "Calling exception continuation..."
                except e) }

这不会在等待时取消。如果你想要那样,你需要能够取消等待操作,例如使用类似的东西:

Async.RunSynchronously(Async.Sleep 3000, cancellationToken = tok)

但我想在这里等待只是为了提问,而你实际上正在做一些其他(可能可取消,可能不可)操作。