9

私は楽しみと利益のために F# を読んでいます - 非同期プログラミングワークフローのキャンセルの下に、次の例があります。

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 ループにあるようです。つまり、キャンセルされた後、for ループまで実行し続けます。これが仕組みですか?睡眠後にチェックしたい場合はどうすればよいですか?

この質問は、上記のようにキャンセルが機能していることを示唆しているようです:キャンセルを明示的にチェックできますか/非同期計算を終了できますか?

編集: これが 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"
    }
4

1 に答える 1

2

インタラクティブな Fsi でのみ F# 2.0 を使用した場合と同じ結果が得られます。同じコードをファイルに入れて実行するfsi cancel.fsxと、出力には最終的なafterものがなく、期待どおりになります。

Fsi v11 と v12 は、コードを実行する両方の方法で予想される出力を示しています。

これは、Fsi v2.0 をインタラクティブに実行する際にバグまたは違いがあることを示唆していますが、これは FSharp の新しいバージョンで修正されています。

于 2014-01-17T15:10:04.310 に答える