День двести восемнадцатый. #ЗаметкиНаПолях
Многопоточность.
9. Async/await. Продолжение
Асинхронные анонимные функции
Асинхронные анонимные функции создаются, как и любой другой анонимный метод или лямбда-выражение, просто добавлением модификатора
Пользовательские типы заданий
В C#5 и 6 асинхронные функции могут возвращать только
Тип
В чем преимущество
В большинстве асинхронных методов это наиболее вероятный случай. В этих случаях
Однако в некоторых случаях, ожидание уже завершенной задачи является наиболее вероятным исходом. И именно здесь полезен
Продолжение следует…
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 5.
Многопоточность.
9. Async/await. Продолжение
Асинхронные анонимные функции
Асинхронные анонимные функции создаются, как и любой другой анонимный метод или лямбда-выражение, просто добавлением модификатора
async
в начале:Func<Task> lambda = async () => await Task.Delay(1000);Делегат должен иметь сигнатуру с типом возврата, подходящим для асинхронного метода (
Func<Task<int>> anonMethod = async delegate()
{
Console.WriteLine("Started");
await Task.Delay(1000);
Console.WriteLine("Finished");
return 10;
};
void
, Task
, Task <TResult>
в C#5 и 6 или пользовательским типом задачи в C#7). Он может захватывать переменные, как и другие анонимные функции, и иметь параметры. Кроме того, асинхронная операция не запускается до тех пор, пока не будет вызван делегат, а несколько вызовов делегата создают несколько операций.Пользовательские типы заданий
В C#5 и 6 асинхронные функции могут возвращать только
void
, Task
или Task <TResult>
. C#7 слегка ослабляет это ограничение и позволяет любому типу, оформленному особым образом, использоваться в качестве возвращаемого типа для асинхронных функций.Тип
System.Threading.ValueTask<TResult>
присутствует в стандартной комплектации только в инфраструктуре netcoreapp2.0, но он также доступен в NuGet пакете System.Threading.Tasks.Extensions
. Он используется в 99,9% случаев. Хотя, можно создать собственный пользовательский тип задания.ValueTask<TResult>
прост: он похож на Task<TResult>
, но это значимый тип. Он имеет метод AsTask
, который позволяет вам получить из него обычную задачу, но в большинстве случаев он используется c await, аналогично Task<TResult>
.В чем преимущество
ValueTask<TResult>
? Всё сводится к выделению памяти в куче и сборке мусора. Task<TResult>
является классом, и хотя в некоторых случаях асинхронная инфраструктура повторно использует завершенные объекты Task<TResult>
, большинству асинхронных методов потребуется создавать новый объект Task<TResult>
. Размещение объектов в .NET достаточно дёшево, поэтому во многих случаях вам не нужно об этом беспокоиться, но если вы делаете это много раз или работаете в условиях жестких ограничений производительности, вы можете избежать такого размещения, когда это возможно. Если асинхронный метод использует выражение await
для чего-то незавершённого, выделение объектов неизбежно. Метод немедленно возвращается, но должен запланировать продолжение для выполнения оставшейся части метода после завершения ожидаемой операции.В большинстве асинхронных методов это наиболее вероятный случай. В этих случаях
ValueTask<TResult>
не даёт никаких преимуществ и может даже быть немного дороже. Однако в некоторых случаях, ожидание уже завершенной задачи является наиболее вероятным исходом. И именно здесь полезен
ValueTask<TResult>
. Например, чтение с использованием буфера (когда размер буфера много больше размера разового чтения). В редких случаях, когда буфер пустой, мы ожидаем завершения чтения в буфер. В остальных случаях (чаще всего) ожидания не требуется, асинхронный метод выполняется без продолжения, и тогда ValueTask<TResult>
выигрывает.Продолжение следует…
Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 5.