есть ли причина, по которой Async.Sleep не может быть немедленно отменен? - PullRequest
8 голосов
/ 28 января 2012

Следующий тест показывает, что Async.Sleep в F # 2.0 нельзя отменить немедленно. Мы получим уведомление об отмене только по истечении времени.

module async_sleep_test
    open System
    open System.Threading
    open System.Threading.Tasks
    open System.Xml

    let cts = new CancellationTokenSource()
    Task.Factory.StartNew(fun () -> 
        try
            Async.RunSynchronously(async{
                printfn "going to sleep"
                do! Async.Sleep(10000)
            }, -1(*no timeout*), cts.Token)
            printfn "sleep completed"
        with 
        | :? OperationCanceledException ->
            printfn "sleep aborted" // we will see it only after 10 sec.
        | _ ->
            printfn "sleep raised error"
    ) |> ignore
    Thread.Sleep(100) // give time to the task to enter in sleep
    cts.Cancel()
    Thread.Sleep(100) // give chance to the task to complete before print bye message
    printfn "press any key to exit...."
    Console.ReadKey(true) |> ignore

Я думаю, что это неправильное поведение. Как вы думаете, это ошибка? Будут ли какие-либо сюрпризы, если, например, я буду использовать следующую реализацию:

static member SleepEx(milliseconds:int) = async{
    let disp = new SerialDisposable()
    use! ch = Async.OnCancel(fun()->disp.Dispose())
    do! Async.FromContinuations(fun (success, error, cancel) ->
        let timerSubscription = new SerialDisposable()
        let CompleteWith = 
            let completed = ref 0
            fun cont ->
                if Interlocked.Exchange(completed, 1) = 0 then
                    timerSubscription.Dispose()
                    try cont() with _->()

        disp.Disposable <- Disposable.Create(fun()->
            CompleteWith (fun ()-> cancel(new OperationCanceledException()))
        )
        let tmr = new Timer(
            callback = (fun state -> CompleteWith(success)), 
            state = null, dueTime = milliseconds, period = Timeout.Infinite
        )
        if tmr = null then
            CompleteWith(fun ()->error(new Exception("failed to create timer")))
        else
            timerSubscription.Disposable <- Disposable.Create(fun()->
                try tmr.Dispose() with _ -> ()
            )
    )
}

Ответы [ 2 ]

6 голосов
/ 28 января 2012

Я бы не сказал, что это ошибка - она ​​вытекает из способа обработки отмены в асинхронных рабочих процессах F # в целом. Как правило, F # предполагает, что примитивные операции, которые вы вызываете с использованием let! или do!, не поддерживают отмену (я полагаю, что в .NET нет стандартного механизма для этого), поэтому F # вставляет проверки отмены до и после вызова, сделанного с использованием let!.

Так что вызов let! res = foo() на самом деле больше похож на следующий (хотя проверки скрыты в реализации библиотеки async):

token.ThrowIfCancellationRequested()
let! res = foo()
token.ThrowIfCancellationRequested()

Конечно, рабочий процесс, возвращаемый foo(), может лучше обрабатывать отмену - обычно, если он реализован с использованием блока async { .. }, он будет содержать больше проверок вокруг каждого let!. Однако в общем случае (если какая-либо операция не реализована более умным способом), отмена будет выполнена после завершения следующего вызова let!.

Ваше альтернативное определение Sleep выглядит для меня довольно неплохо - оно поддерживает отмену лучше, чем то, что доступно в библиотеке F #, и если вам нужно немедленное отмену, то замена F 101 Async.Sleep на SleepEx - единственный способ идти. Однако, вероятно, все еще будут некоторые операции, которые не поддерживают немедленную отмену, поэтому вы можете столкнуться с проблемами в другом месте (если вам нужно такое поведение везде).

PS: Я думаю, что ваша функция SleepEx может быть очень полезна для других. Если бы вы могли поделиться им на веб-сайте F # Snippets , это было бы здорово!

3 голосов
/ 04 января 2013

Я думаю, что это неправильное поведение.Как вы думаете, это ошибка?

Да, я думал, что это ошибка.Я сообщил об этом как об ошибке.Microsoft согласилась, что это ошибка.Они исправили ошибку в F # 3.0 / VS2012 вместе с ошибками в TryScan и других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...