F#异步和TPL任务取消

时间:2015-12-04 11:03:37

标签: asynchronous f# task-parallel-library

我正在研究一个问题,即各种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中显示,但我不知道为什么。

1 个答案:

答案 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