如何处理任务批量中的部分成功?

8

我有三个任务需要并行运行。我需要等待所有三个任务完成,然后继续处理它们的结果。类似于以下代码:

var results = await Task.WhenAll(task1, task2, task3);
return process(results);

当这三项任务全部成功完成时,这种方法可以正常工作。但是,如果其中一项失败了,我会收到一个异常,这不是我想要的。如果这些任务中的任何一个抛出 InvalidOperationException 异常,我可以继续处理,我只需要让 process 方法可以访问被抛出的异常。

我知道我可以在任务内部使用 try...catch,但那似乎不是一个好的解决办法。除了语义上不正确(任务确实失败了-它不应该返回成功),如果我采用这种方法,由于结果可以成功或返回异常,我将不得不返回一个自定义类型。然而,Task<T> 对象已经公开了一个异常通道,因此我宁愿不做重复的工作,而是使用已经存在的内容。


如果我理解正确的话,您想要类似于 var combinedTask = Task.WhenAll(…); await combinedTask.IgnoreExceptions(); return process(combinedTask); 这样的东西吗?其中 IgnoreExceptions() 可以使得在不抛出异常的情况下等待一个失败的 Task。或者 results 应该包含什么内容? - svick
@svick - 我同意,结果似乎不可能是一种一致的类型。我的想法是我需要直接使用任务对象,在这里不使用async/await外观。如果我能确保所有任务都运行完成,无论是否出现错误,那么我就可以处理这些任务对象,但我不确定如何做到这一点。 - George Mauer
@svick - IgnoreExceptions() 是存在的吗?我找不到它。 - George Mauer
不,它不存在。这只是我假设你想要的,带有实际可行的语法。 - svick
2个回答

5
您可以像我的下面的例子一样处理这个案例:
Task<T>[] tasks  = new Task<T>[] { task1, task2, task3 };

try
{
   await Task.WhenAll(tasks);
}
catch(InvalidOperationException exception) // If you need to handle any other exception do it
{
   // handle it if you wan to 
}

// You can use task1.IsFauled to know if current task failed, 
// and you can use task1.Exception to know why it failed

return process(tasks.Where(t => !t.IsFauled).ToArray());

如果任务1运行需要1秒钟,任务2需要2秒钟,任务3需要3秒钟,并且任务1抛出错误。在这种情况下,任务2和任务3是否仍会完成?看起来它们会被取消。 - George Mauer
@GeorgeMauer 他们怎么可能被取消?Task 不可能就这样被取消,那是通过 CancellationTokenSource 完成的,而且 WhenAll() 没有类似的访问方式。 - svick
更新了我的回答。是的,Task.WhenAll将继续执行,直到所有任务完成它们的工作,可能会有一个或多个任务失败,但无论如何,它都会等待所有任务。唯一的区别是,如果其中一个任务抛出异常,await Task.WhenAll(tasks);将在最后抛出AggregateException。之后,您可以使用任务的IsFauled和Exception属性来查找谁失败了以及原因。 - outcoldman
@svick 是的,当然,谢谢,已修复为 InvalidOperationException。我一开始用的是这个,但在第二次更改时改成了 AggregateException,因为我以为我使用的是 Wait() 而不是 await - outcoldman
@GeorgeMauer await Task.WhenAll(tasks); 只会抛出第一个发生的异常。您可以将其更改为 Task.WhenAll(tasks).Wait(),这将抛出 AggregateException,在其中您可以通过 InnerExceptions 属性获取所有异常列表。此外,正如我之前所说,您还可以询问每个任务关于最终状态的 IsFauled 和 Exception 属性。 - outcoldman
显示剩余3条评论

1

如果我理解正确,您正在寻找这个?

var tasks = new[] { task1, task2, task3 };
try { await Task.WhenAll(tasks); } catch (...) { }
return process(tasks);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接