Задачи продолжения (continuation task) или просто «продолжение» — это такие задачи, которые могут вызываться другой задаче (предшествующей), при завершении этой предшествующей задачи. Использование продолжений позволяет организовать цепочки взаимосвязанных задач и передавать некоторые результаты из одной задачи в другую.
Создание продолжения для одной задачи
В примере, представленном ниже, создается задача продолжения, которая выводит в консоль число увеличенное на 1.
using System; using System.Threading.Tasks; namespace Tasks { internal class Program { static void Main(string[] args) { Task<int> task = new Task<int>(() => Increment(11) );//создаем предшествующую задачу Task continuation = task.ContinueWith(x=>Display(task.Result));//создаем продолжение task.Start();//запускаем предшествующую задачу continuation.Wait();//ожидаем окончание продолжения } static void Display(int x) { Console.WriteLine(x); } static int Increment(int a) { return ++a; } } }
Вначале мы создаем предшествующую задачу task
, которая должна возвращать нам результат типа int
(о возвращаемых результатах задачами см. эту статью). Задача продолжения (continuation
) принимает результат от task
и выводит в консоль результат. Задача продолжения создается путем вызова метода ContinueWith
предшествующей задачи. В качестве параметра, ContinueWith
принимает делегат Action<Task<TResult>>
.
В приведенном выше примере представлена простейшая цепочка, состоящая из двух задач: первая задача выполняет сложения, вторая — выводит результат в консоль.
Создание продолжения для нескольких задач
Если необходимо создать продолжение для нескольких задач, то можно воспользоваться одним из следующих статичных методов класса Task
:
WhenAll
— продолжение будет выполнено после завершения всех предшествующих задачWhenAny
— продолжение будет выполнено после завершение какой-либо задачи из списка
или аналогичными методами класса TaskFactory
:
ContinueWhenAll
ContinueWhenAny
Рассмотрим следующий пример:
using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Tasks { internal class Program { public static void Main() { var tasks = new List<Task<int>>(); for (int ctr = 1; ctr <= 10; ctr++) { int baseValue = ctr; tasks.Add(Task.Factory.StartNew(b => (int)b * (int)b, baseValue)); } Task<int[]> results = Task.WhenAll(tasks); int sum = 0; for (int ctr = 0; ctr <= results.Result.Length - 1; ctr++) { var result = results.Result[ctr]; Console.Write($"{result} {((ctr == results.Result.Length - 1) ? "=" : "+")} "); sum += result; } Console.WriteLine(sum); } } }
Для создания задачи продолжения (results
) вызывается статичный метод Task.WhenAll()
. Продолжение отражает результаты десяти своих предшествующих задач, каждой из которых соответствует значение индекса в диапазоне от 1 до 10. Если предшествующие задачи завершаются успешно (их свойство Task.Status
имеет значение RanToCompletion
), то свойство Result
продолжения представляет собой массив значений Task<TResult>.Result
, возвращенных каждой предшествующей задачей. Затем рассчитывается сумма квадратов чисел от 1 до 10 и выводится в консоль:
Итого
Сегодня мы рассмотрели ещё один момент использования библиотеки TPL в .NET C# — задачи продолжения. Используя задачи продолжения мы можем создавать цепочки задач, в которых результаты следующих задач (продолжений) могут основываться на результатах предшествующих задач. Для создания задач продолжения мы можем использовать статичные методы класса Task
или метод ContinueWith
.