Task.WaitAll и исключения - PullRequest
       3

Task.WaitAll и исключения

28 голосов
/ 18 ноября 2010

У меня проблема с обработкой исключений и параллельными задачами.

Код, показанный ниже, запускает 2 задачи и ожидает их завершения.Моя проблема в том, что в случае, если задача выдает исключение, обработчик перехвата никогда не достигается.

        List<Task> tasks = new List<Task>();
        try
        {                
            tasks.Add(Task.Factory.StartNew(TaskMethod1));
            tasks.Add(Task.Factory.StartNew(TaskMethod2));

            var arr = tasks.ToArray();                
            Task.WaitAll(arr);
        }
        catch (AggregateException e)
        {
            // do something
        }

Однако, когда я использую следующий код для ожидания задач с таймаутом, исключение перехватывается.

 while(!Task.WaitAll(arr,100));

Мне кажется, что-то не хватает, так как документация для WaitAll описывает мою первую попытку быть верной.Пожалуйста, помогите мне понять, почему это не работает.

Ответы [ 3 ]

25 голосов
/ 18 ноября 2010

Не могу воспроизвести это - у меня все работает нормально:

using System;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    static void Main()
    {
        Task t1 = Task.Factory.StartNew(() => Thread.Sleep(1000));
        Task t2 = Task.Factory.StartNew(() => {
            Thread.Sleep(500);
            throw new Exception("Oops");
        });

        try
        {
            Task.WaitAll(t1, t2);
            Console.WriteLine("All done");
        }
        catch (AggregateException)
        {
            Console.WriteLine("Something went wrong");
        }
    }
}

Это печатает "Что-то пошло не так", как я и ожидал.

Возможно ли, что одна из ваших задач не выполнена? WaitAll действительно ожидает завершения всех задач, даже если некоторые уже потерпели неудачу.

11 голосов
/ 08 сентября 2011

Вот как я решил проблему, как упоминалось в комментариях к моему ответу / вопросу (см. Выше):

Вызывающий абонент ловит все исключения, возникающие при выполнении задач, координируемых барьером, и сигнализирует другимзадачи с принудительной отменой:

CancellationTokenSource cancelSignal = new CancellationTokenSource();
try
{
    // do work
    List<Task> workerTasks = new List<Task>();
    foreach (Worker w in someArray)
    {
        workerTasks.Add(w.DoAsyncWork(cancelSignal.Token);
    }
    while (!Task.WaitAll(workerTasks.ToArray(), 100, cancelSignal.Token)) ;

 }
 catch (Exception)
 {
     cancelSignal.Cancel();
     throw;
 }
0 голосов
/ 29 января 2013

Я пытался создать вызов для каждого элемента в коллекции, который выглядел примерно так:

var parent = Task.Factory.StartNew(() => {
  foreach (var acct in AccountList)
    {
      var currAcctNo = acct.Number;
      Task.Factory.StartNew(() =>
      {
        MyLocalList.AddRange(ProcessThisAccount(currAcctNo));
      }, TaskCreationOptions.AttachedToParent);
      Thread.Sleep(50);
    }
  });

Мне приходилось добавлять Thread.Sleep после каждого добавления дочерней задачи, потому что еслиЯ не сделал, процесс будет стремиться переписать currAcctNo со следующей итерацией.У меня будет 3 или 4 разных номера счета в моем списке, и при обработке каждого из них вызов ProcessThisAccount покажет последний номер счета для всех вызовов.После того, как я включил Sleep, процесс прекрасно работает.

...