В эпоху повсеместного использования многоядерных процессоров, асинхронного программирования и очень пристальному вниманию пользователей к отзывчивости интерфейсов очень важной характеристикой языка программирования является удобство пользования инструментарием для реализации асинхронности.

Я бы хотел рассказать о такой удобной вещи языка C# как написание своих awaitable типов на примере одного простой но забаной утилиты.



Позвольте кратко напомнить суть async и await.

В .NET 4.0 появился TLP и вместе с ним Task/Task которые представляли собой удобную абстракцию для некоторой CPU или IO bound задачи имеющей или не имеющей результат, а также которая может провалиться выбросив исключение, либо быть отмененной до или во время своего выполнения.
Здесь важно понимать что абстракция по своей природе никак явно не связана с потоками, хотя именно c исполнением операции на отдельном потоке как правило Task и ассоциируется.

Пара примеров использования
Здесь Task.Run запустит исполнение метода CalculatePi на пуле потоков
Task<double> bgTask = Task.Run(() => CalculatePi()); 

А тут мы получили Task в состоянии завершенной задачи прямо из литерала
Task<double> completedTask = Task.FromResult(3.14d); 

А вот здесь мы контролируем Task вручную, с помощью управляющего TaskCompletionSource
TaskCompletionSource<double> taskSource = new TaskCompletionSource<double>();

GlobalPiCalculator.PiCalculated += (pi) => taskSource.SetResult(pi);
GlobalPiCalculator.DoYourJob();

Task<double> manualPiTask = taskSource.Task;




Все это прекрасно, но работа с Task с точки зрения потребителя — не слишком то удобна.
Получили мы Task, но чтобы дождаться его результата нам необходимы Continuation'ы.
В итоге код
Поделиться с друзьями
-->

Комментарии (0)