简体   繁体   中英

Wait for all tasks without throwing exceptions

If I do await Task.WhenAll(tasks) it throws exception if one or more tasks failed. I would like to examine each task objects manually.

Usually it is not good idea to catch(Exception) . Is it reasonable to do it in this case? Or maybe there is another way to wait for all tasks? I cannot catch AggregateException , because if there is just one it will be unwrapped.

How to do it correctly in async method?

The AwaitCompletion extention method below does what you're looking for, with a more generic API. It returns a Task , so you'll still have to await that, either via await or Task.Wait() or something else.

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public static class TaskExtensions
{
    public static Task AwaitCompletion<T>(this ICollection<Task<T>> unfinishedTasks)
    {
        int remainingCount = unfinishedTasks.Count;
        var promise = new TaskCompletionSource<ICollection<Task<T>>>();
        var finishers = unfinishedTasks.Select(x => x.ContinueWith((task, state) =>
        {
            int postCount = Interlocked.Decrement(ref remainingCount);
            if (postCount == 0)
            {
                promise.SetResult(unfinishedTasks);
            }
        }, remainingCount));

        // force evaluation
        var _ = finishers.ToList();

        return promise.Task;
    }

    /// <summary>
    /// Unlike Task.Value, this doesn't wait for completion. Hence, it also doesn't
    /// throw exceptions.
    /// </summary>
    /// <returns>Value if completed, otherwise null (or default value if a value type)</returns>
    public static T GetResultOrDefault<T>(this Task<T> self)
    {
        if (self.IsCompletedSuccessfully)
        {
            return self.Result;
        }
        else
        {
            return default(T);
        }
    }
}

After completion of the returned Task value, all tasks in unfinishedTasks will either be completed or error'd (or still going if you use Task.Wait() with a timeout). AwaitCompletion does not throw exceptions, and awaiting it will only throw exceptions if a timeout is reached. To retrieve exceptions, look at the tasks you passed in.

Usage:

Task[] tasksToWaitFor = ...
await tasksToWaitFor.AwaitCompletion()
foreach (var task in tasksToWaitFor) 
{
    Console.WriteLine("result: {}", task.GetResultOrDefault());
}

The GetResultOrDefault extension method is important. If you hit Value directly, it'll throw AggregateException when the task fails, OTOH GetResultOrDefault will return null.

Swallow the exceptions momentarily with

await Task.WhenAll(tasks).ContinueWith(delegate {} )

but remember to then check all tasks individually and properly surface errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM