#.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. Как вы отменяете асинхронность? Я думаю, что это, вероятно, нуждается в новом вопросе.