thumbmunkeys
thumbmunkeys

Reputation: 20764

Task.WaitAll and Exceptions

I have a problem with exception handling and parallel tasks.

The code shown below starts 2 tasks and waits for them to finish. My problem is, that in case a task throws an exception, the catch handler is never reached.

        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
        }

However when I use the following code to wait for the tasks with a timeout, the exception is caught.

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

I seem to be missing something, as the documentation for WaitAll describes my first attempt to be the correct one. Please help me in understanding why it is not working.

Upvotes: 33

Views: 46212

Answers (3)

Keith Pinster
Keith Pinster

Reputation: 9

I was trying to create a call for each item in a collection, which turned out something like this:

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);
    }
  });

I had to add the Thread.Sleep after each addition of a child task because if I didn't, the process would tend to overwrite the currAcctNo with the next iteration. I would have 3 or 4 distinct account numbers in my list, and when it processed each, the ProcessThisAccount call would show the last account number for all calls. Once I put the Sleep in, the process works great.

Upvotes: 0

gap
gap

Reputation: 2796

Here's how I solved the problem, as alluded to in the comments on my answer/question (above):

The caller catches any exceptions raised by the tasks being coordinated by the barrier, and signals the other tasks with a forced cancellation:

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;
 }

Upvotes: 12

Jon Skeet
Jon Skeet

Reputation: 1499800

Can't reproduce this - it works fine for me:

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");
        }
    }
}

That prints "Something went wrong" just as I'd expect.

Is it possible that one of your tasks isn't finished? WaitAll really does wait for all the tasks to complete, even if some have already failed.

Upvotes: 30

Related Questions