3

The following code run M1, M2, M3, M4 in parallel. Each method may raise exceptions. The method should return the results of the four async methods - either the int returned by methods or the Exceptions.

async Task<string> RunAll()
{
    int m1result, m2result, m3result, m4result;
    try
    {
        var m1task = M1();
        var m2task = M2();
        var m3task = M3();
        var m4task = M4();
        // await Task.WhenAll(new Task<int>[] { m1task, m2task, m3task, m4task });
        m1result = await m1task;
        m2result = await m2task;
        m3result = await m3task;
        m4result = await m4task;
    }
    catch (Exception ex)
    {
        // need to return the ex of the failed task. How?
    }
    // How to implement M1HasException, M2HasException, ... in the following lines?
    var m1msg = M1HasException ? M1ExceptionMessage : m1result.ToString();
    var m2msg = M2HasException ? M2ExceptionMessage : m2result.ToString();
    var m3msg = M3HasException ? M3ExceptionMessage : m3result.ToString();
    var m4msg = M4HasException ? M4ExceptionMessage : m4result.ToString();
    return $"M1: {m1msg}, M2: {m2msg}, M3: {m3msg}, M4: {m4msg}";
}

How to capture the individual exceptions of the failed task?

For example, if only M2 threw an exception,

"M1: 1, M2: Excpetion...., M3: 3, M4: 4"
ca9163d9
  • 27,283
  • 64
  • 210
  • 413

4 Answers4

1

Each task has a Status and and Exception property.

You may want to see if it has faulted:

myTask.Status == TaskStatus.Faulted

Or if it has excepted:

if (myTask.Exception != null) 

You can use ContinueWhenAll to run all the tasks and then check the status.

See the docs here.

Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
  • The problem is as soon as, for example, `M2` got an exception, `M3` and `M4` stopped being invoked. – ca9163d9 Feb 11 '19 at 21:44
0

Could you wrap each await in try-catch block and capture the exception message if any, seems feasible...

var results = new List<string>();

try { results.Add(await t1); } catch { results.Add("Exception"); };
try { results.Add(await t2); } catch { results.Add("Exception"); };
try { results.Add(await t3); } catch { results.Add("Exception"); };

return string.Join("|", results);

if you want to use WhenAll you could await for it and ignore exceptions and then do the same exercise as shown above to retrieve individual task results...

try { await Task.WhenAll(t1, t2, t3); } catch { };
//                                      ^^^^^^^^^
// then same as ^ above
Johnny
  • 8,939
  • 2
  • 28
  • 33
0

As other answers/comments pointed out, one possible approach is by using ContinueWith or ContinueWhenAll. This is a clever trick because Task has the Exception property:

Gets the AggregateException that caused the Task to end prematurely. If the Task completed successfully or has not yet thrown any exceptions, this will return null.

Using ContinueWith whether a task completes successfully or not, it will be passed as the argument to the delegate function. From there you can check if an exception was thrown.

Task<string> GetStringedResult<T>(Task<T> initialTask)
{
    return initialTask.ContinueWith(t => {
        return t.Exception?.InnerException.Message ?? t.Result.ToString();
    });
}

async Task<string> RunAll()
{
    string m1result, m2result, m3result, m4result;

    var m1task = GetStringedResult(M1());
    var m2task = GetStringedResult(M2());
    var m3task = GetStringedResult(M3());
    var m4task = GetStringedResult(M4());

    m1result = await m1task;
    m2result = await m2task;
    m3result = await m3task;
    m4result = await m4task;

    return $"M1: {m1result}, M2: {m2result}, M3: {m3result}, M4: {m4result}";
}
Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42
0

You can wrap the tasks inside a WaitAll and catch the AggregateException (docs),

try
{
    Task.WaitAll(new[] { task1, task2 }, token);
}
catch (AggregateException ae)
{
    foreach (var ex in ae.InnerExceptions)
        //Do what ever you want with the ex.
}
Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91