Как реализовать ожидание без Async CTP
-
28-10-2019 - |
Вопрос
Как бы вы реализовали что-то, что работает аналогично ключевому слову Async CTP await
?Существует ли простая реализация, которая работает как await
во всех случаях, или же await
требует разных реализаций для разных сценариев?
Решение
await
всегда включает в себя одно и то же преобразование, но это довольно болезненное.Часть кода await
библиотека не слишком сложна, но сложность заключается в том, что компилятор создает для вас конечный автомат, позволяя продолжить переход обратно в нужное место.
Возможно , что из-за моего хакерского использования блоков итератора (yield return) вы могли подделать что-то подобное ... но это было бы довольно некрасиво.
Несколько недель назад я провел веб-семинар DevExpress о том, что компилятор делает за кулисами -который показывает декомпилированный код из пары примеров, а также объясняет, как компилятор создает задачу для возврата и что должен делать «ожидающий».Это может быть вам полезно.
Другие советы
Новое ключевое слово await
имеет семантику, аналогичную существующему ключевому слову yield return
, в том смысле, что они оба заставляют компилятор генерировать для вас конечный автомат стиля продолжения. Таким образом, можно что-то взломать вместе, используя итераторы, которые имеют примерно такое же поведение, что и Async CTP.
Вот как это будет выглядеть.
родовое слово Поскольку yield return
генерирует код IEnumerable
, наша сопрограмма должна возвращать код IEnumerable
. Вся магия происходит внутри метода AsyncHelper.Invoke
. Это то, что запускает нашу сопрограмму (маскирующуюся под взломанный итератор). Особое внимание уделяется тому, чтобы итератор всегда выполнялся в текущем контексте синхронизации, если он существует, что важно при попытке смоделировать, как await
работает в потоке пользовательского интерфейса. Это достигается за счет синхронного выполнения первого кода MoveNext
и последующего использования кода SynchronizationContext.Send
для выполнения остальной части рабочего потока, который также используется для асинхронного ожидания отдельных шагов.
Все, что касалось TaskCompletionSource
, было моей попыткой воспроизвести способ, которым await
может "возвращать" значение. Проблема в том, что сопрограмма должна фактически возвращать код сгенерированного кода, поскольку это не что иное, как взломанный итератор. Поэтому мне нужно было придумать альтернативный механизм для захвата возвращаемого значения.
Здесь есть некоторые очевидные ограничения, но я надеюсь, что это дает вам общее представление. Он также демонстрирует, как CLR может иметь один обобщенный механизм для реализации сопрограмм, для которых IEnumerable
и await
будут использоваться повсеместно, но по-разному для обеспечения соответствующей семантики.
Существует несколько реализаций и примеров сопрограмм, сделанных из итераторов (yield).
Одним из примеров является платформа Caliburn.Micro, которая использует этот шаблон для асинхронных операций с графическим интерфейсом.Но его можно легко обобщить для общего асинхронного кода.
Структура MindTouch DReAM реализует сопрограммы поверх шаблона Iterator, который функционально очень похож на Async /Жду:
родовое словопротив
родовое слово Result
- это версия кода Task
для DReAM.DLL-файлы фреймворка работают с .NET 2.0+, но для их создания вам потребуется 3.5, поскольку в наши дни мы используем синтаксис 3.5.
Билл Вагнер из Microsoft написал статью в журнале MSDN о том, каквы можете использовать библиотеку параллельных задач в Visual Studio 2010 для реализации асинхронного поведения без добавления зависимости от асинхронного ctp.
Он широко использует Task
и Task<T>
, что также дает дополнительное преимущество, заключающееся в том, что после выхода C # 5 ваш код будет хорошо подготовлен для использования async
и await
.
Насколько я понимаю, основные различия между yield return
и await
заключаются в том, что await
может явно возвращать новое значение в продолжение.
в то время как с yield return
вам нужно будет выполнить то же самое по ссылке.