Когда f# async проверяет свой CancellationToken?

Я читаю 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: что происходит со следующим циклом, если один спит 200 мс, 2500 мс и 4000 мс соответственно? Он печатает середину?

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"
    }

person johv    schedule 17.01.2014    source источник
comment
Что касается вашего редактирования, Fsi v2.0 печатает ..middle.., а Fsi v11 и v12 - нет.   -  person Leaf Garland    schedule 17.01.2014
comment
Интересно! Отличие F# 2.0 от F# 3.0? Упоминается ли это где-то в каком-то релизе? Или это только в fsi.exe (кажется маловероятным)?   -  person johv    schedule 17.01.2014
comment
Только в интерактивном Fsi компиляция вашего кода в F# 2.0 дает правильные результаты. Я не видел упоминания об этом раньше.   -  person Leaf Garland    schedule 17.01.2014


Ответы (1)


Я вижу те же результаты, что и вы, с F # 2.0, только в интерактивном Fsi. Если я помещу тот же код в файл и запущу fsi cancel.fsx, то вывод не будет иметь окончательного after и будет таким, как вы ожидаете.

Fsi v11 и v12 показывают ожидаемый результат для обоих способов выполнения кода.

Это говорит о том, что при интерактивном запуске Fsi v2.0 существует некоторая ошибка или отличие, которое было исправлено в более поздней версии FSharp.

person Leaf Garland    schedule 17.01.2014