2

I need help with a simple Task.WhenAll C# code. I have upto 50 different tasks being fired at the same time, however, some of these calls could return an error message.

I am trying to write the exception handling code, so that I can process the ones that worked (which it does), but capture the ones that errored, so I can perform additional code against them.

There is AggregateException, but is there a way to see which call/inputs that created that exception?

I'm unable to share the actual code due to strict company policy, but an example is as follows:

List<ListDetails> libs = getListDetails();
var tasks = new Task<List<ItemDetails>>[libs.Count];
for (int i = 0; i < libs.Count; i++)
{
    tasks[i] = getListItems(libs[i].ServerRelativeUrl, libs[i].ListId);
}

try
{
    await Task.WhenAll(tasks);
}
catch(AggregateException aex)
{
    //Capture which Server RelativeUrls and ListIDs that failed.    
}
Cann0nF0dder
  • 524
  • 1
  • 6
  • 17

3 Answers3

6

You can query the original tasks after waiting:

// Tasks taken from any source.
Task[] myTasks = ...;

// Logically wait ("await") for all tasks to complete,
// while ignoring possible exceptions (`Task.WhenAny` doesn't throw).
await Task.WhenAny(Task.WhenAll(myTasks));

// All tasks have completed - examine their outcome as you see fit.
foreach (var task in myTasks) {
 if (myTask.Status == RanToCompletion)
     ...
}

Please note that a previous version of this answer was incorrect. The core idea was sound, but the code contained a mistake.

usr
  • 168,620
  • 35
  • 240
  • 369
4

You should check the InnerExceptions property, or you can use AggregateException.Flatten method as shown in the docs:

try {
     task1.Wait();
  }
  catch (AggregateException ae) {
     foreach (var e in ae.Flatten().InnerExceptions) {
        if (e is CustomException) {
           Console.WriteLine(e.Message);
        }
        else {
           throw;
        }
     }
  }

This will give you all the exceptions that are thrown by the child tasks but unfortunately there is no way to know which task threw a particular exception. You will need to investigate the stack traces.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • does `task1.Wait()` blocks the UI theard.? for my case, the app goes to non-responsive state. Is there an alternative way to get aggregateException without UI being non-responsive.? – Ankit Dhadse Jan 02 '20 at 09:47
  • @AnkitDhadse Yes, `task.Wait()` and `Task.WaitAll(tasks)` are synchronous (blocking), unlike `await task` and `await Task.WhenAll(tasks)`. And `await` unwraps aggregate exceptions, returning the first inner exception. But there are ways to run a set of tasks asynchronously and yet still throw an aggregate exception. See [this answer](https://stackoverflow.com/a/55664013/5405967) or others on that page. – MarredCheese Dec 03 '21 at 20:44
0

You should make a array of async tasks then when you are going to add each task to array you should place a anonymous function with lambda expression in ContinueWith(){}. Here is an example :

var tasks = new List<Task>(); 
tasks.add(t.ContinueWith(o =>
{
    if(o.IsFaulted){
        Console.WriteLine(o.Exception?.InnerException);
        return;
        }

        Console.WriteLine(o.Result);
  }));

 tasks.WhenAll().ContinueWith(o=>{

Console.WriteLine("All Done!")
});