F# 异步和 TPL 任务取消
F# async and TPL task cancellation
我正在调查各种 C# 异步方法和 F# 异步函数之间的方法调用链挂起程序的问题。我认为下面的 F# 脚本通过混合 F# async 和 TPL 重现了同样的问题:
open System
open System.Threading
open System.Threading.Tasks
let testy = async {
let tokenSource = new CancellationTokenSource();
Task.Run(fun () ->
printfn "Inside first task"
Thread.Sleep(1000)
printfn "First task now cancelling the second task"
tokenSource.Cancel()) |> ignore
let! result =
Task.Factory.StartNew<int>((fun () ->
printfn "Inside second task"
Thread.Sleep(2000)
printfn "Second task about to throw task cancelled exception"
tokenSource.Token.ThrowIfCancellationRequested()
printfn "This is never reached, as expected"
0),
tokenSource.Token)
|> Async.AwaitTask
return result }
printfn "Starting"
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
printfn "Program ended with result: %d" result
运行这个程序在FSI中挂了解释器。在挂起之前我得到以下输出:
Starting
Inside first task
Inside second task
First task now cancelling the second task
Second task about to throw task cancelled exception
我发现如果我换行
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
到
let result = testy |> Async.RunSynchronously
然后它不再挂起并且 "OperationCanceledException" 按预期显示在 FSI 中,但我不知道为什么。
正在将取消令牌传递给 Task.Factory.StartNew
。因此,当它被取消时,Async.StartAsTask
将永远不会启动并始终报告 WaitingForActivation
的状态。如果令牌未传递给 Task.Factory.StartNew
,则状态将更改为 'Faulted',这将取消阻止 Async.AwaitTask
,这将允许 Async.RunSynchronously
重新抛出异常。
修复
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
需要将相同的取消令牌传递给 Async.StartAsTask
。
let result =
Async.StartAsTask (testy, TaskCreationOptions.None, tokenSource.Token)
|> Async.AwaitTask
|> Async.RunSynchronously
我正在调查各种 C# 异步方法和 F# 异步函数之间的方法调用链挂起程序的问题。我认为下面的 F# 脚本通过混合 F# async 和 TPL 重现了同样的问题:
open System
open System.Threading
open System.Threading.Tasks
let testy = async {
let tokenSource = new CancellationTokenSource();
Task.Run(fun () ->
printfn "Inside first task"
Thread.Sleep(1000)
printfn "First task now cancelling the second task"
tokenSource.Cancel()) |> ignore
let! result =
Task.Factory.StartNew<int>((fun () ->
printfn "Inside second task"
Thread.Sleep(2000)
printfn "Second task about to throw task cancelled exception"
tokenSource.Token.ThrowIfCancellationRequested()
printfn "This is never reached, as expected"
0),
tokenSource.Token)
|> Async.AwaitTask
return result }
printfn "Starting"
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
printfn "Program ended with result: %d" result
运行这个程序在FSI中挂了解释器。在挂起之前我得到以下输出:
Starting
Inside first task
Inside second task
First task now cancelling the second task
Second task about to throw task cancelled exception
我发现如果我换行
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
到
let result = testy |> Async.RunSynchronously
然后它不再挂起并且 "OperationCanceledException" 按预期显示在 FSI 中,但我不知道为什么。
正在将取消令牌传递给 Task.Factory.StartNew
。因此,当它被取消时,Async.StartAsTask
将永远不会启动并始终报告 WaitingForActivation
的状态。如果令牌未传递给 Task.Factory.StartNew
,则状态将更改为 'Faulted',这将取消阻止 Async.AwaitTask
,这将允许 Async.RunSynchronously
重新抛出异常。
修复
let result = testy |> Async.StartAsTask |> Async.AwaitTask |> Async.RunSynchronously
需要将相同的取消令牌传递给 Async.StartAsTask
。
let result =
Async.StartAsTask (testy, TaskCreationOptions.None, tokenSource.Token)
|> Async.AwaitTask
|> Async.RunSynchronously