Язык программирования C#9 и платформа .NET5
await Task.WhenAny(task1, task2, task3);В результате вывод становится таким:
Fun With Async ===>Done with work!Void method completedDone with second task!CompletedDone with third task!Done with first task!Вызов асинхронных методов из неасинхронных методов
В каждом из предшествующих примеров ключевое слово
использовалось для возвращения в поток вызывающего кода, пока выполняется асинхронный метод. В целом ключевое словоasyncможет применяться только в методе, помеченном какawait. А что если вы не можете (или не хотите) помечать метод с помощьюasync?asyncК счастью, существуют другие способы вызова асинхронных методов. Если вы просто не используете ключевое слово
, тогда код продолжает работу после асинхронного метода, не возвращая управление вызывающему коду. Если вам необходимо ожидать завершения асинхронного метода (что происходит, когда применяется ключевое словоawait), то существуют два подхода.awaitПервый подход предусматривает просто использование свойства
с методами, возвращающимиResult, или методаTask<T>с методами, возвращающимиWait(). (Вспомните, что метод, который возвращает значение, обязан возвращатьTask/Task<T>, будучи асинхронным, а метод, не имеющий возвращаемого значения, возвращаетTask<T>, когда является асинхронным.) Если метод терпит неудачу, то возвращаетсяTask.AggregateExceptionМожете также добавить вызов
, который обеспечивает такой же эффект, как ключевое словоGetAwaiter().GetResult()в асинхронном методе, и распространяет исключения в той же манере, что иawait. Тем не менее, указанные методы помечены в документации как "не предназначенные для внешнего использования", а это значит, что они могут измениться либо вовсе исчезнуть в какой-то момент в будущем. Вызовasync/awaitработает как с методами, возвращающими значение, так и с методами без возвращаемого значения.GetAwaiter().GetResult()На заметку! Решение использовать свойство
или вызовResultсGetAwaiter().GetResult()возлагается полностью на вас, и большинство разработчиков принимают решение, основываясь на обработке исключений. Если ваш метод возвращаетTask<T>, тогда вы должны применять вызовTaskилиGetAwaiter().GetResult().Wait()Например, вот как вы могли бы вызывать метод
:DoWorkAsync()Console.WriteLine(<b>DoWorkAsync().Result</b>);Console.WriteLine(<b>DoWorkAsync().GetAwaiter().GetResult()</b>);Чтобы остановить выполнение до тех пор, пока не произойдет возврат из метода с возвращаемым типом
, просто вызовите методvoidна объектеWait():TaskMethodReturningVoidAsync().Wait();Ожидание с помощью await в блоках catch и finally
В версии C# 6 появилась возможность помещения вызовов await в блоки
иcatch. Для этого сам метод обязан бытьfinally. Указанная возможность демонстрируется в следующем примере кода:asyncstatic async Task<string> MethodWithTryCatch(){try{//Do some workreturn "Hello";}catch (Exception ex){await LogTheErrors();throw;}finally{await DoMagicCleanUp();}}Обобщенные возвращаемые типы в асинхронных методах (нововведение в версии 7.0)
До выхода версии C# 7 возвращаемыми типами методов
были толькоasync,TaskиTask<T>. В версии C# 7 доступны дополнительные возвращаемые типы при условии, что они следуют шаблону с ключевым словомvoid. В качестве конкретного примера можно назвать типasync. Введите код, подобный показанному ниже:ValueTaskstatic async ValueTask<int> ReturnAnInt(){await Task.Delay(1_000);return 5;}К типу
применимы все те же самые правила, что и к типуValueTask, посколькуTask— это просто объектValueTaskдля типов значений, заменяющий собой принудительное размещение объекта в куче.TaskЛокальные функции (нововведение в версии 7.0)
Локальные функции были представлены в главе 4 и использовались в главе 8 с итераторами. Они также могут оказаться полезными для асинхронных методов. Чтобы продемонстрировать преимущество, сначала нужно взглянуть на проблему. Добавьте новый метод по имени
со следующим кодом:MethodWithProblems()