Создание продолжения задачи
Одной из новаторских и очень удобных особенностей библиотеки TPL является возможность создавать продолжение задачи. Продолжение — это одна задача, которая автоматически начинается после завершения другой задачи. Создать продолжение можно, в частности, с помощью метода ContinueWith(), определенного в классе Task. Ниже приведена простейшая форма его объявления:
public Task ContinueWith(Action<Task> действие_продолженмя)
где действие_продолжения обозначает задачу, которая будет запущена на исполнение по завершении вызывающей задачи. У делегата Action имеется единственный параметр типа Task. Следовательно, вариант делегата Action, применяемого в данном методе, выглядит следующим образом.
public delegate void Action<in T>(T obj)
В данном случае обобщенный параметр Т обозначает класс Task.
Продолжение задачи демонстрируется на примере следующей программы.
// Продемонстрировать продолжение задачи.
using System;
using System.Threading;
using System.Threading.Tasks;
class ContinuationDemo {
// Метод, исполняемый как задача,
static void MyTask() {
Console.WriteLine("MyTask() запущен");
for(int count = 0; count < 5; count++) {
Thread.Sleep(500);
Console.WriteLine("В методе MyTask() подсчет равен " + count );
}
Console.WriteLine("MyTask завершен");
}
// Метод, исполняемый как продолжение задачи,
static void ContTask(Task t) {
Console.WriteLine("Продолжение запущено");
for(int count = 0; count < 5; count++) {
Thread.Sleep(500);
Console.WriteLine("В продолжении подсчет равен " + count );
}
Console.WriteLine("Продолжение завершено");
}
static void Main() {
Console.WriteLine("Основной поток запущен.");
// Сконструировать объект первой задачи.
Task tsk = new Task(MyTask);
//А теперь создать продолжение задачи.
Task taskCont = tsk.ContinueWith(ContTask);
// Начать последовательность задач,
tsk.Start();
// Ожидать завершения продолжения.
taskCont.Wait();
tsk.Dispose();
taskCont.Dispose();
Console.WriteLine("Основной поток завершен.");
}
}
Ниже приведен результата выполнения данной программы.
Основной поток запущен.
MyTask() запущен
В методе MyTask() подсчет равен 0
В методе MyTask() подсчет равен 1
В методе MyTask() подсчет равен 2
В методе MyTask() подсчет равен 3
В методе MyTask() подсчет равен 4
MyTask завершен
Продолжение запущено
В продолжении подсчет равен 0
В продолжении подсчет равен 1
В продолжении подсчет равен 2
В продолжении подсчет равен 3
В продолжении подсчет равен 4
Продолжение завершено
Основной поток завершен.
Как следует из приведенного выше результата, вторая задача не начинается до тех пор, пока не завершится первая. Обратите также внимание на то, что в методе Main() пришлось ожидать окончания только продолжения задачи. Дело в том, что метод MyTask() как задача завершается еще до начала метода ContTask как продолжения задачи. Следовательно, ожидать завершения метода MyTask() нет никакой надобности, хотя если и организовать такое ожидание, то в этом будет ничего плохого.
Любопытно, что в качестве продолжения задачи нередко применяется лямбда-выражение. Для примера ниже приведен еще один способ организации продолжения задачи из предыдущего примера программы.
//В данном случае в качестве продолжения задачи применяется лямбда-выражение.
Task taskCont = tsk.ContinueWith((first) =>
{
Console.WriteLine("Продолжение запущено");
for(int count = 0; count < 5; count++) {
Thread.Sleep (500);
Console.WriteLine("В продолжении подсчет равен " + count );
}
Console.WriteLine("Продолжение завершено");
}
);
В этом фрагменте кода параметр first принимает предыдущую задачу (в данном случае — tsk).
Помимо метода ContinueWith(), в классе Task предоставляются и другие методы, поддерживающие продолжение задачи, обеспечиваемое классом TaskFactory. К их числу относятся различные формы методов ContinueWhenAny() и ContinueWhenAll(), которые продолжают задачу, если завершится любая или все указанные задачи соответственно.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК