等待所有任务而不抛出异常

时间:2018-01-29 09:12:44

标签: c# async-await

如果我await Task.WhenAll(tasks),如果一个或多个任务失败,则抛出异常。我想手动检查每个任务对象。

catch(Exception)通常不是一个好主意。在这种情况下这样做是否合理?或许还有另一种方法可以等待所有任务?我无法抓住AggregateException,因为如果只有一个,它将被解开。

如何在异步方法中正确执行?

3 个答案:

答案 0 :(得分:0)

因此,如果您的一个或多个任务可能遇到问题,您希望其他任务继续完成

public async Task WaitAllEvenIfException(this Task[] tasksToWaitFor)
{
    // wait until all completed, even if one of them raises exception
    While (!tasksToWaitFor.All(task => task.IsCompleted)
    {
         // not all completed yet
         try
         {
              await Task.WhenAll(tasksToWaitFor);
         }
         catch (AggregateException exc)
         {
             // one or more of the Tasks threw exception,
             // don't handle now, wait until all Completed
         }
    }
}

用法:

Task[] tasksToWaitFor = ...
await this.WaitAllEvenIfException(tasksToWaitFor);

// now all completed, you can use the State to check which ones threw exception

IEnumerable<Task> tasksThatThrewExeption =  tasksToWaitFor
        .Where(task => task.Status == TaskStatus.Faulted);
foreach (Task exceptedTask in tasksThatThrewException)
{
    Exception exc = exceptedTask.Exception;
    // handle exception
}

答案 1 :(得分:-1)

下面的AwaitCompletion扩展方法可以使用更通用的API来满足您的需求。它会返回Task,因此您仍然必须通过awaitTask.Wait()或其他方式等待。

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

在完成返回的Task值之后,unfinishedTasks中的所有任务都将完成或出错(或者,如果您将Task.Wait()用于超时,则仍将继续)。 AwaitCompletion不会引发异常,等待它只会在达到超时时引发异常。要检索异常,请查看您传递的任务。

用法:

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

GetResultOrDefault扩展方法很重要。如果您直接点击Value,则在任务失败时它将抛出AggregateException,OTOH GetResultOrDefault将返回null。

答案 2 :(得分:-2)

暂时吞下异常
await Task.WhenAll(tasks).ContinueWith(delegate {} )

但请记住,然后单独检查所有任务并正确显示错误。