Это одновременное , в том смысле, что многие невыполненные асинхронные операции могут выполняться в любое время. Может быть или не быть многопоточным .
По умолчанию await
запланирует продолжение обратно в «текущий контекст выполнения». «Текущий контекст выполнения» определяется как SynchronizationContext.Current
, если это не null
, или TaskScheduler.Current
, если нет SynchronizationContext
.
Вы можете переопределить это поведение по умолчанию, вызвав ConfigureAwait
и передав false
для параметра continueOnCapturedContext
. В этом случае продолжение не будет запланировано обратно в этот контекст выполнения. Обычно это означает, что он будет запущен в потоке потоков.
Если вы не пишете код библиотеки, поведение по умолчанию - именно то, что вам нужно. WinForms, WPF и Silverlight (то есть все инфраструктуры пользовательского интерфейса) предоставляют SynchronizationContext
, поэтому продолжение выполняется в потоке пользовательского интерфейса (и может безопасно получать доступ к объектам пользовательского интерфейса). ASP.NET также предоставляет SynchronizationContext
, который обеспечивает выполнение продолжения в правильном контексте запроса.
Другие потоки (включая потоки пулов потоков, Thread
и BackgroundWorker
) не предоставляют SynchronizationContext
. Поэтому консольные приложения и службы Win32 по умолчанию вообще не имеют SynchronizationContext
. В этой ситуации продолжения выполняются в потоках пула потоков. Вот почему демонстрационные версии консольных приложений, использующие await
/ async
, включают вызов Console.ReadLine
/ ReadKey
или блокировку Wait
для Task
.
.
Если вам нужен SynchronizationContext
, вы можете использовать AsyncContext
из моей библиотеки Nito.AsyncEx ; в основном это просто async
-совместимый «основной цикл» с SynchronizationContext
. Я считаю его полезным для консольных приложений и модульных тестов (VS2012 теперь имеет встроенную поддержку async Task
модульных тестов).
Дополнительную информацию о SynchronizationContext
см. В статье Моя февральская MSDN .
Никогда не вызывается DoEvents
или его эквивалент; скорее поток управления возвращает полностью, а продолжение (остальная часть функции) планируется запустить позже. Это гораздо более чистое решение, потому что оно не вызывает проблем с повторным входом, как если бы вы использовали DoEvents
.