Выполнение задач параллельно

Хорошо, так что в основном у меня есть множество задач (10), и я хочу начать их все в одно и то же время и дождаться их завершения. По завершении я хочу выполнить другие задачи. Я прочитал кучу ресурсов об этом, но я не могу понять это в своем конкретном случае …

Вот что я сейчас (код был упрощен):

public async Task RunTasks() { var tasks = new List { new Task(async () => await DoWork()), //and so on with the other 9 similar tasks } Parallel.ForEach(tasks, task => { task.Start(); }); Task.WhenAll(tasks).ContinueWith(done => { //Run the other tasks }); } //This function perform some I/O operations public async Task DoWork() { var results = await GetDataFromDatabaseAsync(); foreach (var result in results) { await ReadFromNetwork(result.Url); } } 

Поэтому моя проблема заключается в том, что, когда я жду, когда задачи будут завершены WhenAll , он сообщает мне, что все задачи завершены, хотя ни одна из них не завершена. Я попытался добавить Console.WriteLine в свой foreach и когда я вошел в задачу продолжения, данные продолжают поступать из моей предыдущей Task , которые на самом деле не закончены.

Что я здесь делаю неправильно?

Вы почти никогда не должны использовать конструктор Task напрямую. В вашем случае эта задача вызывает только ту задачу, которую вы не можете ждать.

Вы можете просто вызвать DoWork и вернуть задачу, сохранить ее в списке и дождаться завершения всех задач. Имея в виду:

 tasks.Add(DoWork()); // ... await Task.WhenAll(tasks); 

Тем не менее, асинхронные методы выполняются синхронно до тех пор, пока не будет достигнуто первое ожидание по незавершенной задаче. Если вы беспокоитесь о том, что эта часть занимает слишком много времени, используйте Task.Run чтобы разгрузить ее в другой stream ThreadPool а затем сохраните эту задачу в списке:

 tasks.Add(Task.Run(() => DoWork())); // ... await Task.WhenAll(tasks); 

По сути, вы смешиваете две несовместимые асинхронные парадигмы; т.е. Parallel.ForEach() и async-await .

Для чего вы хотите, сделайте то или другое. Например, вы можете просто использовать Parallel.For[Each]() и вообще отказаться от async-ожидания. Parallel.For[Each]() будет возвращаться только после завершения всех параллельных задач, а затем вы можете перейти к другим задачам.

В коде есть и другие проблемы:

  • вы отмечаете метод async, но не ожидаете в нем (ожидание, которое у вас есть, есть в делетете, а не в методе);

  • вы почти наверняка хотите .ConfigureAwait(false) на вашем .ConfigureAwait(false) , особенно если вы не пытаетесь использовать результаты сразу в streamе пользовательского интерфейса.

Если вы хотите запустить параллельную параллельность этих задач в разных streamах с помощью TPL, вам может понадобиться что-то вроде этого:

 public async Task RunTasks() { var tasks = new List> { DoWork, //... }; await Task.WhenAll(tasks.AsParallel().Select(async task => await task())); //Run the other tasks } 

Этот подход распараллеливает только небольшой объем кода: очередь процесса в пул streamов и возврат незавершенной Task . Кроме того, для такого небольшого количества задач распараллеливание может занимать больше времени, чем просто работать асинхронно. Это может иметь смысл только в том случае, если ваши задачи выполняют более длительную (синхронную) работу до их первого ожидания.

В большинстве случаев лучшим способом будет:

 public async Task RunTasks() { await Task.WhenAll(new [] { DoWork(), //... }); //Run the other tasks } 

На мой взгляд, в вашем коде:

  1. Вы не должны обертывать свой код в Task перед переходом на Parallel.ForEach .

  2. Вы можете просто await Task.WhenAll вместо использования ContinueWith .

Метод DoWork является асинхронным методом ввода-вывода. Это означает, что вам не нужно несколько streamов для выполнения нескольких из них, так как большую часть времени метод будет асинхронно ждать завершения ввода-вывода. Одного streamа достаточно, чтобы сделать это.

 public async Task RunTasks() { var tasks = new List { DoWork(), //and so on with the other 9 similar tasks }; await Task.WhenAll(tasks); //Run the other tasks } 

Вы почти никогда не должны использовать конструктор Task для создания новой задачи. Чтобы создать асинхронную задачу ввода-вывода, просто вызовите метод async . Чтобы создать задачу, которая будет выполнена в streamе пула streamов, используйте Task.Run . Вы можете прочитать эту статью для подробного объяснения Task.Run и других вариантов создания задач.

Просто добавьте блок try-catch вокруг Task.WhenAll

NB: генерируется экземпляр System.AggregateException, который действует как shell вокруг одного или нескольких исключений, которые произошли. Это важно для методов, которые координируют несколько задач, таких как Task.WaitAll () и Task.WaitAny (), поэтому AggregateException может обернуть все исключения в выполняемых задачах, которые произошли.

 try { Task.WaitAll(tasks.ToArray()); } catch(AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source)); } } 
  • Вызов синхронного асинхронного метода
  • В чем разница между возвратом пустоты и возвратом задачи?
  • Шаблон для самоотдачи и перезапуска задачи
  • Очередь процесса с многопоточным или задачами
  • Должны ли мы переключиться на использование асинхронного ввода-вывода по умолчанию?
  • Выполнение задачи в фоновом режиме в приложении WPF
  • ConfigureAwait подталкивает продолжение в stream пула
  • Interesting Posts

    Как настроить макет, когда появляется мягкая клавиатура

    Как найти самый низкий общий предк двух узлов в любом двоичном дереве?

    Как я могу конвертировать адрес IPV6 в адрес IPV4?

    Можно ли использовать std :: basic_string в качестве непрерывного буфера при таргетинге на C ++ 03?

    Создание образа диска диска C и запуск на виртуальной машине VMware

    Каков наилучший способ ограничить IP-адреса, которые могут быть доступны из контейнера докеров?

    Переопределение переменных-членов в Java

    Использование DocumentFilter.FilterBypass

    Просмотр рабочего стола Xubuntu в полноэкранном режиме под Virtualbox в Windows 8

    Как управляются, реализуются, распределяются кучи и стеки памяти?

    Почему геттер называется так много раз с помощью атрибута rendered?

    Почему массивы в C распадаются на указатели?

    Почему ковариация и контравариантность не поддерживают тип ценности

    Внедрить Mixin в Java?

    Как убить задачи в Windows 7, когда даже диспетчер задач не откроет или не ответит?

    Давайте будем гением компьютера.