Ori*_*rds 2 c# asynchronous task
给定以下内容:
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();
Run Code Online (Sandbox Code Playgroud)
对task.Wait()的调用将引发一个AggregateException,其内部异常包含fail1和fail2异常。但是如何获得tPass1成功的结果?
这可能吗?
我知道可以在完成后从单个任务中获取结果WhenAll,tPass1.Result但是可以通过一种方法将它们放入数组中,而不必手动跟踪送入的所有内容WhenAll?
也许
public async Task<Task[]> RejectFailedFrom(params Task[] tasks)
{
try
{
await Task.WhenAll(tasks);
}
catch(Exception exception)
{
// Handle failed tasks maybe
}
return tasks.Where(task => task.Status == TaskStatus.RanToCompletion).ToArray();
}
Run Code Online (Sandbox Code Playgroud)
用法
var tasks = new[]
{
Task.FromResult(1),
Task.FromException<int>(new ArgumentException("fail1")),
Task.FromException<int>(new ArgumentException("fail2"))
};
var succeed = await RejectFailedFrom(tasks);
// [ tasks[0] ]
Run Code Online (Sandbox Code Playgroud)
当任务失败时,我们无法访问它的Result属性,因为它会抛出异常。因此,要获得部分成功WhenAll任务的结果,我们必须确保该任务能够成功完成。那么问题就变成了如何处理失败的内部任务的异常。吞下它们可能不是一个好主意。至少我们想记录它们。这是一个WhenAll从不抛出但返回ValueTuple结构中的结果和异常的替代方案的实现。
public static Task<(T[] Results, Exception[] Exceptions)> WhenAllEx<T>(
params Task<T>[] tasks)
{
tasks = tasks.ToArray(); // Defensive copy
return Task.WhenAll(tasks).ContinueWith(t => // 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(); // Flatten the hierarchy of AggregateExceptions
if (exceptions.Length == 0 && t.IsCanceled)
{
// No exceptions and at least one task was canceled
exceptions = new[] { new TaskCanceledException(t) };
}
return (results, exceptions);
}, default, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
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))}");
Run Code Online (Sandbox Code Playgroud)
输出:
状态:RanToCompletion
结果:1
异常:fail1、fail2