Страницы

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

суббота, 8 февраля 2020 г.

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

#c_sharp #net #многопоточность


Есть несколько объектов типа 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 предлагает для ожидания выполнения других задач?
    


Ответы

Ответ 1



Существует несколько способов решения предложенной задачи. Есть статический метод 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.

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

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