Задачи продолжения (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:
ContinueWhenAllContinueWhenAny
Рассмотрим следующий пример:
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.