Асинхронный.AwaitEvent не отменяется после вызова базового события

#.net #events #asynchronous #f#

#.net #Мероприятия #асинхронный #f#

Вопрос:

Я пытаюсь понять асинхронность.Поведение в ожидании событий. Согласно документации:

Создает асинхронное вычисление, которое ожидает одного вызова события CLI путем добавления обработчика к событию. Как только вычисление завершается или отменяется, обработчик удаляется из события.

и из раздела замечаний:

Вычисление будет реагировать на отмену во время ожидания события. Если происходит отмена и указано cancelAction, то оно выполняется, и вычисление продолжает ожидать события. Если cancelAction не задано, то отмена приводит к немедленной отмене вычисления.

Я не указываю никакого действия отмены и поэтому ожидаю, что событие будет обработано один раз, и как только это произойдет, обработчик должен быть отписан. Но если я запущу этот пример, я смогу увидеть, что это событие обрабатывается снова и снова.

 open Microsoft.FSharp.Control

let timer = new System.Timers.Timer(2000.0)
timer.AutoReset <- true
timer.Start()

let fn = async {
    let timerEvent = Async.AwaitEvent timer.Elapsed

    for _ in [1..10] do
        let! x = timerEvent
        printfn "elapsed event occurred at %O" x.SignalTime
}

fn |> Async.RunSynchronously
 

Вывод:

 elapsed event occurred at 19.06.2014 23:46:21
elapsed event occurred at 19.06.2014 23:46:23
elapsed event occurred at 19.06.2014 23:46:25
elapsed event occurred at 19.06.2014 23:46:27
elapsed event occurred at 19.06.2014 23:46:29
elapsed event occurred at 19.06.2014 23:46:31
elapsed event occurred at 19.06.2014 23:46:33
elapsed event occurred at 19.06.2014 23:46:35
elapsed event occurred at 19.06.2014 23:46:37
elapsed event occurred at 19.06.2014 23:46:39
 

Отличается ли это поведение от документированного? Если нет, то почему?

Ответ №1:

timerEvent имеет тип Async<ElapsedEventArgs> . В общем, an Async<'something> — это просто представление асинхронного вычисления, которое вы могли бы запускать в будущем, возможно, неоднократно. Подобная конструкция let! — это то, как Async<'something> на самом деле выполняется.

Это похоже на то, как let f() = printfn "wibble" просто определяется функция f , которая будет печатать «колебание» при каждом запуске.

Итак, что здесь происходит, так это то, что let! x = timerEvent при каждом запуске вы снова подписываетесь на событие и ждете, когда оно произойдет.

Как только событие происходит, эта конкретная подписка отменяется, но управление продолжается после перехода let! к printfn , а затем снова по циклу и создается новая подписка.

Комментарии:

1. Это имеет смысл, спасибо. Я также пытался получить cancelAction вызов, но без какого-либо успеха. Я переопределил timerEvent следующим образом let timerEvent = Async.AwaitEvent(timer.Elapsed, fun () -> printfn "cancelled") и "cancelled" никогда не печатался. Не могли бы вы посоветовать?

2. Как вы отменяете асинхронность? Я думаю, что это, вероятно, нуждается в новом вопросе.