给出以下内容:
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
成功的结果?
这可能吗?
我知道WhenAll
完成后,可以通过tPass1.Result
从单个任务中获取结果,但是有一种方法可以将它们放入数组中,而不必手动跟踪所有东西都喂到WhenAll
了?
答案 0 :(得分:5)
也许
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();
}
用法
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] ]
答案 1 :(得分:3)
当任务失败时,我们将无法访问其Result
属性,因为该属性会抛出。因此,要获得部分成功的WhenAll
任务的结果,我们必须确保该任务将成功完成。然后,问题就变成了如何处理失败的内部任务。吞下它们可能不是一个好主意。至少我们要记录它们。这是一个备选WhenAll
的实现,该替代方法永不抛出,但以ValueTuple
结构形式返回结果和异常。
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
答案 2 :(得分:1)
与@Theodor Zoulias强大而优雅的solution一起玩耍,使我有所收获。它看起来很黑,但仍然可以使用。可以继续Task.WhenAll
使用不会肯定会引发异常的内容(例如_ => { }
)和Wait
这样的内容。
var cts = new CancellationTokenSource();
cts.Cancel();
var canceled = Task.Run(() => 1, cts.Token);
var faulted = Task.FromException<int>(new Exception("Some Exception"));
var ranToCompletion = Task.FromResult(1);
var allTasks = new[] { canceled, faulted, ranToCompletion };
// wait all tasks to complete regardless anything
Task.WhenAll(allTasks).ContinueWith(_ => { }).Wait();
foreach(var t in allTasks)
{
Console.WriteLine($"Task #{t.Id} {t.Status}");
if (t.Status == TaskStatus.Faulted)
foreach (var e in t.Exception.InnerExceptions)
Console.WriteLine($"\t{e.Message}");
if (t.Status == TaskStatus.RanToCompletion)
Console.WriteLine($"\tResult: {t.Result}");
}
输出看起来像这样:
Task #2 Canceled
Task #1 Faulted
Some Exception
Task #5 RanToCompletion
Result: 1
答案 3 :(得分:1)