Страницы

Поиск по вопросам

пятница, 12 апреля 2019 г.

Выполнение действия после завершения нескольких Task

Есть несколько объектов типа Task или Task. Необходимо дождаться завершения работы каждого из этих объектов и выполнить действия по завершению. Можно воспользоваться методом Task.Wait, например так:
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
t1.Wait(); t2.Wait();
Console.WriteLine("Tasks completed"); }
private static void DoSomething2() { Console.WriteLine("Do something 2"); }
private static void DoSomething1() { Console.WriteLine("Do something 1"); }
Однако при большом количестве задач это будет выглядеть достаточно громоздко. Какие способы инфраструктура задач .NET предлагает для ожидания выполнения других задач?


Ответ

Существует несколько способов решения предложенной задачи.
Есть статический метод Task.WaitAll, который принимает в качестве аргументов массив задач и блокирует вызывающий поток до завершения всех этих задач. Кроме того, есть перегрузки этого метода, принимающие таймаут ожидания (и возвращающие признак, завершились ли задачи в указанное время) и/или токен отмены ожидания. Также есть перегрузка с параметром, принимающим переменное количество аргументов (ключевое слово params):
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
Task.WaitAll(t1, t2); Console.WriteLine("Tasks completed"); }
private static void DoSomething2() { Console.WriteLine("Do something 2"); }
private static void DoSomething1() { Console.WriteLine("Do something 1"); }
Не надо путать метод Task.WaitAll с методом Task.WaitAny, ожидающим выполнения любой из переданных задач.
Если есть возможность писать, используя .NET 4.5, то можно воспользоваться ключевыми словами async/await для асинхронного ожидания выполнения задач. Метод Task.WhenAll создаст задачу, которая завершится, когда завершатся все задачи, переданные ему в качестве аргументов.
Код ниже будет выполняться синхронно, пока выполнение не дойдёт до инструкции await. В этот момент управление будет передано коду, вызвавшему метод Foo, а ожидание выполнения задач будет происходить в другом потоке. По завершению выполнения задач управление будет возвращено методу Foo и он продолжит работу:
public static async void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
await Task.WhenAll(t1, t2); Console.WriteLine("Tasks completed"); }
private static void DoSomething2() { Console.WriteLine("Do something 2"); }
private static void DoSomething1() { Console.WriteLine("Do something 1"); }
Кроме того, задача, порождаемая методом Task.WhenAll, является обычной задачей, а значит её можно синхронно подождать, используя метод Task.Wait
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
Task.WhenAll(t1, t2).Wait(); Console.WriteLine("Tasks completed"); }
private static void DoSomething2() { Console.WriteLine("Do something 2"); }
private static void DoSomething1() { Console.WriteLine("Do something 1"); }
Ну и наконец, задачу порождённую методом Task.WhenAll можно продолжить, используя метод Task.ContinueWith. Этод метод запускает задачу, переданную ему в качестве аргумента, после завершения заданной задачи. В задаче-продолжении при это можно обработать результаты и исключения задач, которые изначально необходимо было подождать:
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
var continuation = Task.WhenAll(t1, t2).ContinueWith( t => { Console.WriteLine("Tasks completed"); });
continuation.Wait(); }
private static void DoSomething2() { Console.WriteLine("Do something 2"); }
private static void DoSomething1() { Console.WriteLine("Do something 1"); }
P.S. Отдельное спасибо @NickVolynkin за предложение задать вопрос и самому ответить на него и @PavelMayorov за идею с ожиданием через Task.WhenAll

Комментариев нет:

Отправить комментарий