Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Согласно рекомендациям команды создателей .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

Если асинхронный метод должен поддерживать

await
, тогда используйте необобщенный класс
Task
и опустите любые операторы
return
, например:

static async Task MethodReturningTaskOfVoidAsync

{

await Task.Run( => { /* Выполнить какую-то работу... */

Thread.Sleep(4_000);

});

Console.WriteLine("Void method completed");

// Метод завершен

}

Затем в коде,

вызывающем этот метод, примените ключевое слово
await
:

MethodReturningVoidAsync;

Console.WriteLine("Void method complete");

Асинхронные методы, возвращающие void и работающие в стиле "запустил и забыл"

Если метод должен быть асинхронным, но не обязан поддерживать

await
и применяться в сценариях "запустил и забыл", тогда добавьте ключевое слово
async
и сделайте возвращаемым типом
void
, а не
Task
. Методы такого рода обычно используются для задач вроде ведения журнала, когда нежелательно, чтобы запись в журнал приводила к задержке выполнения остального кода.

static async void MethodReturningVoidAsync

{

await Task.Run( => { /* Выполнить какую-то работу... */

Thread.Sleep(4_000);

});

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!");

// Третья задача завершена!

}

Здесь каждая задача всего лишь приостанавливает текущий поток на некоторый период времени; тем не менее, посредством таких задач может быть представлена любая единица работы (обращение к веб-службе, чтение базы данных или что-нибудь еще). Еще один вариант предусматривает ожидание не каждой отдельной задачи, а всех их вместе. Это более вероятный сценарий, когда имеются три работы (скажем, проверка поступления сообщений электронной почты, обновление сервера, загрузка файлов), которые должны делаться в пакете, но могут выполняться параллельно. Ниже приведен модифицированный код, в котором используется метод

Task.WhenAll
:

static async Task MultipleAwaits

{

var task1 = Task.Run( =>

{

Thread.Sleep(2_000);

Console.WriteLine("Done with first task!");

});

var task2=Task.Run( =>

{

Thread.Sleep(1_000);

Console.WriteLine("Done with second task!");

Поделиться с друзьями: