给出以下内容:
var tPass1 = Task.FromResult(1); var tFail1 = Task.FromException<int>(new ArgumentException("fail1")); var tFail2 = Task.FromException<int>(new ArgumentException("fail2")); var task = Task.WhenAll(tPass1, tFail1, tFail2); task.Wait();对task.Wait()的调用将抛出一个AggregateException,其内部异常包含fail1和fail2异常.但是如何获得tPass1成功的结果?
the call to task.Wait() throws an AggregateException, whose inner exceptions contain the fail1 and fail2 exceptions. But how can I access the tPass1 successful result?
这可能吗?
我知道在WhenAll完成之后我可以通过tPass1.Result从单个任务中获得结果,但是有一种方法可以将它们放入数组中,从而不必手动跟踪所有进给的东西进入WhenAll?
I'm aware that I can get the result from the individual task after the WhenAll has finished, via tPass1.Result however is there a way to get them in an array to avoid having to manually track all the things feeding into the WhenAll?
推荐答案任务失败时,我们将无法访问其 Result 属性,因为它会抛出.因此,要获得部分成功完成的WhenAll任务的结果,我们必须确保该任务将成功完成.然后,问题就变成了如何处理失败的内部任务.吞下它们可能不是一个好主意.至少我们要记录它们.这是一个替代性WhenAll的实现,该替代性不会抛出任何异常,但是会在 ValueTuple 结构.
When a task fails we cannot access its Result property because it throws. So to have the results of a partially successful WhenAll task, we must ensure that the task will complete successfully. The problem then becomes what to do with the exceptions of the failed internal tasks. Swallowing them is probably not a good idea. At least we would like to log them. Here is an implementation of an alternative WhenAll that never throws, but returns both the results and the exceptions in a ValueTuple struct.
public static Task<(T[] Results, Exception[] Exceptions)> WhenAllEx<T>(params Task<T>[] tasks) { return Task.WhenAll(tasks).ContinueWith(_ => // return a continuation of WhenAll { var results = tasks .Where(t => t.Status == TaskStatus.RanToCompletion) .Select(t => t.Result) .ToArray(); var aggregateExceptions = tasks .Where(t => t.IsFaulted) .Select(t => t.Exception) // The Exception is of type AggregateException .ToArray(); var exceptions = new AggregateException(aggregateExceptions).Flatten() .InnerExceptions.ToArray(); // Trick to flatten the hierarchy of AggregateExceptions return (results, exceptions); }, TaskContinuationOptions.ExecuteSynchronously); }用法示例:
var tPass1 = Task.FromResult(1); var tFail1 = Task.FromException<int>(new ArgumentException("fail1")); var tFail2 = Task.FromException<int>(new ArgumentException("fail2")); var task = WhenAllEx(tPass1, tFail1, tFail2); task.Wait(); Console.WriteLine($"Status: {task.Status}"); Console.WriteLine($"Results: {String.Join(", ", task.Result.Results)}"); Console.WriteLine($"Exceptions: {String.Join(", ", task.Result.Exceptions.Select(ex => ex.Message))}");输出:
状态:RanToCompletion 结果:1 例外:fail1,fail2
Status: RanToCompletion Results: 1 Exceptions: fail1, fail2
警告:上面的实现会忽略已取消的任务,因此不会模仿 Task.WhenAll :
Caution: the implementation above ignores canceled tasks, and so does not mimic the default behavior of Task.WhenAll:
如果提供的任务均未故障,但至少其中一项被取消,则返回的任务将以Canceled状态结束.
在这种情况下,更一致的行为是返回单个TaskCanceledException.可以通过在WhenAllEx内的最后return命令之前添加以下代码来完成此操作:
A more consistent behavior would be to return a single TaskCanceledException in this case. This can be done by adding this code inside the WhenAllEx, just before the final return command:
if (exceptions.Length == 0) { var canceledTask = tasks.FirstOrDefault(t => t.IsCanceled); if (canceledTask != null) { exceptions = new[] { new TaskCanceledException(canceledTask) }; } }更多推荐
一个任务失败时,是否可以从Task.WhenAll获得成功的结果?
发布评论