我想运行这样的程序并等待它,作为一个整体:
Task task1 = Task.Factory.StartNew(() => MethodThatCouldThrow());
Task task2 = task1.ContinueWith(t => HandleFailure(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
注意,已经有一些关于这个主题的讨论(下面的链接),但它们并没有完全回答这个问题:
- 如何在一件事上等待以确保工作完成(*工作被定义为主要任务加上可能的延续)
如何避免正常情况下的异常处理。正常情况为:
- 任务1已完成(RanToCompletion),而任务2未运行(已取消)
- task1出错(Faulted),但task2成功处理(RanToCompletion)(其他情况不正常,应该抛出,例如task2错误)
为什么我不能只等待任务2?因为如果task1成功,它将被"取消"。
为什么我不能只等待任务1?因为如果它失败了(task2可能仍在运行),它就是"Faulted"。
为什么我不能同时等待这两项任务?因为如果task1成功,那么task2将被"取消",并且此等待将抛出。
几个可能的解决方案(但它们不满足所需的条件):
- 无条件延续,检查要做什么
- 条件延续但更复杂的等待,例如,在task2上等待,如果task1成功,则忽略cancellation异常
我能找到的相关线程:
等待具有OnlyOnFaulted Continuation的任务会导致AggregateException
使用具有条件延续的任务
谢谢!
对于您的第一点,使用Task.WhenAll(task1,task2).Wait()
就足够了。
至于你的第二点,你可以忽略任务2被取消,就像一样
combinedTask.ContinueWith(_=>{
if (_.IsFaulted){
if (typeof(_.Exception) == typeof(TaskCancelledException){
// Do Nothing
}
}
})
如果你知道task1失败的异常类型,你可以用同样的方法处理它。根据您将任务链接在一起的方式,它只能产生AggregateException,这意味着您需要对InnnerException进行异常类型检查,直到您遇到非AggregateException 类型的异常
您可能应该先等到这两项任务都完成然后使用您喜欢的任何谓词检查任务,并在此基础上做出决定。
您可以通过简单地忽略等待抛出的任何异常来等待这两个任务(我认为这是肮脏的,因为它是基于异常的控制流),或者:
var combinedTask = Task.WhenAll(task1, task2);
var combinedTaskNoError = combinedTask.ContinueWith(_ => { }, TCO.ExecuteSynchronously);
await combinedTaskNoError; //Will not throw.
中间一行可能应该封装在一个辅助方法中。我称之为WhenCompleted
。