2. Синхронный вызов
• var result = service.DoSomething(source);
Код
• Максимально простой и понятный способ сделать что-либо
Плюсы
• Поток занят все время до получения результата
• Занятость GUI-потока – второй смертный грех перед пользователем
• Для веб-платформ еще хуже: висит интерфейс браузера
• Для мобильных платформ совсем плохо: процесс после нескольких
секунд будет принудительно выгружен
Минусы
4. EAP как антипаттерн
• Результат не удастся локализовать в вызывающем методе
• Необходимость отписки от события не даст использовать лямбдуГлобальность
• Нет никаких гарантий, что по событию не придет чужой ответ
• Нет, никаких гарантий, что по событию другому не придет ваш ответ
• Разрешение этих ситуаций требует специальных мер по передаче и
распознаванию состояния в событии
Временная
зависимость
• Объем кода на порядок выше, чем простой синхронный вызов
• Вызов раскидан по коду класса в трех местах – минимум два метода и
поле
Неудобочитаемость
5. Asynchronous Programming Model
•Метод resultType DoSomething(source) -> IAsyncResult BeginDoSomething(source,
AsyncCallback completed)
•Добавляется метод TResult EndDoSomething(asyncResult)
•Опционально добавляется метод (лямбда) c параметром IAsyncResult
Модификации
•Метод BeginDoSomething возвращает управление немедленно
•Метод EndDoSomething ждет окончания асинхронной операции и возвращает ее результат
(включая возможный выброс исключения)
•Метод обратного вызова получает управление после завершения задачи в другом потоке
•IAsyncResult позволяет получить переданное произвольное состояние, узнать, завершилась
ли операция, дождаться завершения по событию
Поведение
•BeginDoSomething(source, asyncResult => {var result = EndSomething(AsyncResult);
HandleResult(result)})Код
6. Плюсы и минусы APM
• Универсальность – паттерн покрывает практически любые асинхронные
вызовы без специальных ухищрений
• Многие библиотеки используют именно этот паттерн
• WCF генерирует асинхронные вызовы по этому паттерну
• Многие библиотеки требуют от пользователя использовать этот паттерн
Плюсы
• По сравнению с более современными решениями синтаксис громоздкий
• Синхронизация кода в методе обратного вызова – наша проблема
• Поддержка прерывания выполнения длительной операции – тоже наша
проблема
• Логически связанный код «до» и «после» делится на два разных блока –
затруднено использование using и т.п.
• Этот паттерн официально объявлен устаревшим
Минусы
7. Task-based Asynchronous Pattern
• TResult DoSomething(source) -> Task<TResult> DoSomething(source)
• Дополнительно в асинхронный метод можно передать токен отмены
• Если операция позволяет следить за прогрессом – добавляется параметр
IProgress<T>
Модификации
• Стандартный способ отмены выполнения
• Совместимость с APM, стандартные методы конвертации туда и обратно
• Стандартная асинхронная реализация контроля прогресса
• Этот метод рекомендован официально
• TaskCompletionSource позволяет превратить в задачу что угодно
Плюсы
• Асинхронная обработка результата все-таки в методе обратного вызова со
всеми вытекающимиМинусы
8. Async Enumerator
•Это клиентский паттерн, работающий поверх APM
•Вместо указания метода обратного вызова используется оператор yield return
•Код после yield return выполняется в тот же момент, что и обратный вызов завершения
операции
•Возвращаемое число соответствует числу ожидаемых асинхронных результатов
•Для возврата результата используется метод AsyncEnumerator.DequeueAsyncResult()
Модификации
•IEnumerable<int> AsyncDoSomething(AsyncEmumerator asyncEnumerator)
{ service.BeginDoSomething(source, asyncEnumerator.End());
yield return 1;
var result = service.DoEndSomething(asyncEnumerator.DequeueAsyncResult()); }
• AsyncEnumerator asyncEnumerator = new AsyncEnumerator();
asyncEnumerator.Execute(AsyncDoSomething(asyncEnumerator));
Код
•Код с методами обратного вызова превращается в линейный
•Не нужно никаких специальных мер для выполнения обработки результатов в том же
контексте потока
•Для прерывания ожидания результатов достаточно вызвать yield break
•Сам вызов итератора соответсвует паттерну APM
•Отсутствует дополнительное потребление ресурсов
Плюсы
9. Async/Await
•Async/Await – синтаксический сахар .NET 4.5, решающий ту же задачу, что и AsyncEnumerable
•Любой метод, который возвращает Task<TResult> можно вызвать result = await
DoSomethingAsync(source)
•Все, что после await, будет выполнено после завершения задачи
•Метод с await внутри должен быть помечен async и возвращать задачу или void
•В его коде создавать задачу не надо - .NET сделает это за вас
Модификация
•Все плюшки AsyncEnumerator в сочетании с прозрачным кодом
•Нет ограничений на try..catch
•Вся мощь TPL и здесь к нашим услугам
•Минимальное время отклика и потребление ресурсов без лишнего шумового кода
•await не требует именно задачу – достаточно реализации метода GetAwaiter
•Можно получить await/async на .NET 4.0
Плюсы
•Если не нужно выполнение продолжения в контексте вызова – надо много
ConfigureAwait(false)
•Если нельзя заставлять ждать вызывающий поток – вся цепочка вызовов должна быть async
•await не пробрасывает AggregateException
•.NET 4.0 надо патчить
Минусы