忽略在Task.WhenAll上抛出异常的任务,只获取完成的结果

时间:2016-09-20 08:51:25

标签: c# .net exception task-parallel-library task

我正在处理一个任务并行问题,我有很多可能会或可能不会抛出异常的任务。

我想处理正确完成的所有任务并记录其余的任务。 Task.WaitAll支持Task异常,但不允许我收集其余结果。

    static readonly Task<string> NormalTask1 = Task.FromResult("Task result 1");
    static readonly Task<string> NormalTask2 = Task.FromResult("Task result 2");
    static readonly Task<string> ExceptionTk = Task.FromException<string>(new Exception("Bad Task"));
    var results = await Task.WaitAll(new []{ NormalTask1,NormalTask2,ExceptionTk});

Task.Waitall抛出ExcceptionTk的例外,忽略其余结果。我怎样才能得到忽略异常的结果并同时记录异常?

我可以将任务包装到try{...}catch(){...}内部异常的另一个任务中,但我无法访问它们,我希望我不必添加此开销。

3 个答案:

答案 0 :(得分:12)

您可以创建一个这样的方法来代替Task.WhenAll

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{    
    return Task.WhenAll(
        tasks.Select(
            task => task.ContinueWith(
                t => t.IsFaulted
                    ? new ResultOrException<T>(t.Exception)
                    : new ResultOrException<T>(t.Result))));
}


public class ResultOrException<T>
{
    public ResultOrException(T result)
    {
        IsSuccess = true;
        Result = result;
    }

    public ResultOrException(Exception ex)
    {
        IsSuccess = false;
        Exception = ex;
    }

    public bool IsSuccess { get; }
    public T Result { get; }
    public Exception Exception { get; }
}

然后你可以检查每个结果,看它是否成功。

编辑:上面的代码没有处理取消;这是另一种实现方式:

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{    
    return Task.WhenAll(tasks.Select(task => WrapResultOrException(task)));
}

private async Task<ResultOrException<T>> WrapResultOrException<T>(Task<T> task)
{
    try
    {           
        var result = await task;
        return new ResultOrException<T>(result);
    }
    catch (Exception ex)
    {
        return new ResultOrException<T>(ex);
    }
}

答案 1 :(得分:2)

您可以从其属性Task<TResult>中获取每个成功完成的Result的结果。

var NormalTask1 = Task.FromResult("Task result 1");
var NormalTask2 = Task.FromResult("Task result 2");
var ExceptionTk = Task.FromException<string>(new Exception("Bad Task"));

Task<string>[] tasks = new[] { NormalTask1, NormalTask2, ExceptionTk };
Task whenAll = Task.WhenAll(tasks);
try
{
    await whenAll;
}
catch
{
    if (whenAll.IsFaulted) // there is also the possibility of being canceled
    {
        foreach (var ex in whenAll.Exception.InnerExceptions)
        {
            Console.WriteLine(ex); // Log each exception
        }
    }
}

string[] results = tasks
    .Where(t => t.Status == TaskStatus.RanToCompletion)
    .Select(t => t.Result)
    .ToArray();
Console.WriteLine($"Results: {String.Join(", ", results)}");

输出:

System.Exception:错误的任务
结果:任务结果1,任务结果2

答案 2 :(得分:0)

您可以添加带有异常处理的HOC,然后检查成功。

 class Program
{
    static async Task Main(string[] args)
    {
        var itemsToProcess = new[] { "one", "two" };
        var results = itemsToProcess.ToDictionary(x => x, async (item) =>
        {
            try
            {
                var result = await DoAsync();
                return ((Exception)null, result);
            }
            catch (Exception ex)
            {
                return (ex, (object)null);
            }
        });

        await Task.WhenAll(results.Values);

        foreach(var item in results)
        {
            Console.WriteLine(item.Key + (await item.Value).Item1 != null ? " Failed" : "Succeed");
        }
    }

    public static async Task<object> DoAsync()
    {
        await Task.Delay(10);
        throw new InvalidOperationException();
    }
}