Согласно рекомендациям команды создателей .NET Core при разработке прикладного кода (Windows Forms, WPF и т.д.) следует полагаться на стандартное поведение, а в случае написания неприкладного кода (скажем, библиотеки) использовать вызов
ConfigureAwait(false)
. Одним исключением является инфраструктура ASP.NET Core (рассматриваемая в части IX), где специальная реализация
SynchronizationContext
не создается; таким образом, вызов
ConfigureAwait(false)
не дает преимущества при работе с другими инфраструктурами.
Соглашения об именовании асинхронных методов
Конечно же, вы заметили, что мы изменили имя метода с
DoWork
на
DoWorkAsync
, но по какой причине? Давайте
предположим, что новая версия метода по-прежнему называется
DoWork
, но вызывающий код реализован так:
// Отсутствует ключевое слово await!
string message = DoWork;
Обратите внимание, что мы действительно пометили метод ключевым словом
async
, но не указали ключевое слово await при вызове
DoWork
. Здесь мы получим ошибки на этапе компиляции, потому что возвращаемым значением
DoWork
является объект
Task
, который мы пытаемся напрямую присвоить переменной типа
string
. Вспомните, что ключевое слово
await
отвечает за извлечение внутреннего возвращаемого значения, которое содержится в объекте
Task
. Поскольку
await
отсутствует, возникает несоответствие типов.
На заметку! Метод, поддерживающий
await
— это просто метод, который возвращает
Task
или
Task<T>
.
С учетом того, что методы, которые возвращают объекты
Task
, теперь могут вызываться в неблокирующей манере посредством конструкций
async
и
await
, в Microsoft рекомендуют (в качестве установившейся практики) снабжать имя любого метода, возвращающего
Task
, суффиксом
Async
. В таком случае разработчики, которым известно данное соглашение об именовании, получают визуальное напоминание о том, что ключевое слово
await
является обязательным, если они намерены вызывать метод внутри асинхронного контекста.
На заметку! Обработчики событий для элементов управления графического пользовательского интерфейса (вроде обработчика события
Click
кнопки), а также методы действий внутри приложений в стиле MVC, к которым применяются ключевые слова
async
и
await
, не следуют указанному соглашению об именовании.
Асинхронные методы, возвращающие void
В настоящий момент наш метод
DoWorkAsync
возвращает объект
Task
, содержащий "реальные данные" для вызывающего кода, которые будут получены прозрачным образом через ключевое слово
await
. Однако что если требуется построить асинхронный метод, возвращающий
void
? Реализация зависит о того, нуждается метод в применении
await
или нет (как в сценариях "запустил и забыл").
Асинхронные методы, возвращающие void и поддерживающие await
Асинхронные методы, возвращающие void и работающие в стиле "запустил и забыл"
Если метод должен быть асинхронным, но не обязан поддерживать
await
и применяться в сценариях "запустил и забыл", тогда добавьте ключевое слово
async
и сделайте возвращаемым типом
void
, а не
Task
. Методы такого рода обычно используются для задач вроде ведения журнала, когда нежелательно, чтобы запись в журнал приводила к задержке выполнения остального кода.
Console.WriteLine("Fire and forget void method completed");
// Метод завершен
}
Затем в коде, вызывающем этот метод, ключевое слово
await
не используется:
MethodReturningVoidAsync;
Console.WriteLine("Void method complete");
Асинхронные методы с множеством контекстов await
Внутри реализации асинхронного метода разрешено иметь множество контекстов
await
. Следующий код является вполне допустимым:
static async Task MultipleAwaits
{
await Task.Run( => { Thread.Sleep(2_000); });
Console.WriteLine("Done with first task!");
// Первая задача завершена!
await Task.Run( => { Thread.Sleep(2_000); });
Console.WriteLine("Done with second task!");
// Вторая задача завершена!
await Task.Run( => { Thread.Sleep(2_000); });
Console.WriteLine("Done with third task!");
// Третья задача завершена!
}
Здесь каждая задача всего лишь приостанавливает текущий поток на некоторый период времени; тем не менее, посредством таких задач может быть представлена любая единица работы (обращение к веб-службе, чтение базы данных или что-нибудь еще). Еще один вариант предусматривает ожидание не каждой отдельной задачи, а всех их вместе. Это более вероятный сценарий, когда имеются три работы (скажем, проверка поступления сообщений электронной почты, обновление сервера, загрузка файлов), которые должны делаться в пакете, но могут выполняться параллельно. Ниже приведен модифицированный код, в котором используется метод