在 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)
但我想在这里等待只是为了提问,而你实际上正在做一些其他(可能可取消,可能不可)操作。
我试图通过我用 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)
但我想在这里等待只是为了提问,而你实际上正在做一些其他(可能可取消,可能不可)操作。