foxwendy
foxwendy

Reputation: 2929

How to make exception be awared of before calling Task.WaitAll?

In my case, I created tasks by:

IList<Task> Tasks = new List<Task>();
Tasks.Add(Task.Run(async () => { await functionAsync();}));

I need these tasks to run infinitely in order to continuously processing some incoming data. However, in cases when some fatal error/exceptions happen, I need to end the program by canceling all the tasks. I put together an simple example to simulate what I intended to do , but it does not work. I thought a task will considered to be end when throw an exception, and the WaitAny should return with AggregatedException, but it doesn't seem to be how it actually works. So, how can I make it correct?

public static void Main(string[] args)
{
    Console.WriteLine(nameof(Main));
    for (int i = 1; i < 5; i++)
    {
        var i1 = i;
        _tasks.Add(Task.Run(async () => { await Tryme(i1); }));
    }

    try
    {
        Task.WaitAny(_tasks.ToArray());
    }
    catch (Exception e)
    {
        Console.WriteLine("Stop my program if any of the task in _tasks throw exception");
        Console.WriteLine(e);
    }
    Console.ReadLine();
}

private static async Task Tryme(int i)
{

    Console.WriteLine($"I'm {i}");
    if (i == 3)
    {
        Console.WriteLine($"{i} is throwing the exception");
        throw new Exception("fake one");
    }
    await Task.Delay(TimeSpan.MaxValue);
}

Upvotes: 1

Views: 199

Answers (2)

foxwendy
foxwendy

Reputation: 2929

Got some clue from this post . It looks that WaitAny will not throw any exception. I got the exception by:

int faultIndex = Task.WaitAny(_tasks.ToArray());
if (_tasks[faultIndex].IsFaulted)
{
     Console.WriteLine($"{faultIndex} end");
     throw _tasks[faultIndex].Exception;
}

Upvotes: 0

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

Instead of manually canceling the entire task-chain, you can use TPL Dataflow which falts the entire block for you if an unhandeled exception occurs, or can be configured to behave in other modes if needed:

var actionBlock = new ActionBlock<int>(i => TryMe(i));

foreach (var num in Enumerable.Range(0, 100))
{
   actionBlock.Post(num);
}

try
{
   await actionBlock.Completion();
}
catch (Exception e)
{
    // Block finished prematurely, handle exception.
}

Note Dataflow takes care of parralisation for you, no need to manually create tasks.

Upvotes: 3

Related Questions