f#async何时检查其CancellationToken?

时间:2014-01-17 14:38:17

标签: asynchronous f# cancellationtokensource cancellation-token

我正在阅读F# for fun and profit - Asynchronous programming。在取消工作流程下,他们有以下示例:

let testLoop = async {
    for i in [1..100] do
    // do something
    printf "%i before.." i

    // sleep a bit 
    do! Async.Sleep 10  
    printfn "..after"
    }

open System
open System.Threading

// create a cancellation source
let cancellationSource = new CancellationTokenSource()

// start the task, but this time pass in a cancellation token
Async.Start (testLoop,cancellationSource.Token)

// wait a bit
Thread.Sleep(200)  

// cancel after 200ms
cancellationSource.Cancel()

关于这一点,他们说:

  

在F#中,任何嵌套的异步调用都会自动检查取消令牌!

     

在这种情况下,它是一行:

do! Async.Sleep(10) 
     

从输出中可以看出,此行是取消发生的地方。

然而,对我来说(VS2010,F#2.0,F#Interactive)我得到以下输出。请注意我取消令牌后如何打印..after。他们完全错了吗?

1 before....after
2 before....after
3 before....after
4 before....after
5 before....after
6 before....after
7 before....after
8 before....after
9 before....after
10 before....after
11 before....after
12 before....after
13 before..
val cancellationSource : CancellationTokenSource

>
..after

因此,当进入 Async.Sleep时,是否可以检查取消?不,那就打印出来了:

13 before....after
14 before..
val cancellationSource : CancellationTokenSource

>

所以看起来支票实际上是在for-loop中!即在取消后它一直运行到for循环。这是怎么回事?那么如果我宁愿在睡眠后检查呢?

这个问题似乎暗示了取消工作,就像我上面描述的那样:Can I explicitly check for cancellation / terminate async computation?

编辑: 关于这是否只在FSI 2.0中:如果一个人分别睡200ms,2500ms和4000ms,下一个循环会发生什么?它打印在中间吗?

let testLoop = async {
    for i in [1..5] do
    printf "%i before.." i
    do! Async.Sleep 2000
    printfn "..middle.."
    do! Async.Sleep 1000  
    printfn "..after"
    }

1 个答案:

答案 0 :(得分:1)

我只在交互式Fsi中看到与F#2.0相同的结果。如果我将相同的代码放在一个文件中并运行fsi cancel.fsx,那么输出就没有最终的after,而是你所期望的。

Fsi v11和v12显示了运行代码的两种方式的预期输出。

这表明在交互式运行Fsi v2.0时存在一些错误或差异,这在后来的FSharp版本中得到修复。