我正在研究一个问题,即各种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
然后它不再挂起并且&#34; OperationCanceledException&#34;如预期的那样在FSI中显示,但我不知道为什么。
答案 0 :(得分:4)
取消令牌正在传递给Task.Factory.StartNew
。因此,当它被取消时,Async.StartAsTask
永远不会启动并始终报告WaitingForActivation
的状态。如果令牌未传递给Task.Factory.StartNew
,则状态将更改为“已故障”,这将取消阻止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