我正试图围绕C#中的并行任务的一些语法和结构,特别是围绕chaining multiple tasks和handling errors。
我想要创建的具体步骤顺序是:
Process1()
Process1()
完成且没有错误,请执行Process2()
Process2()
完成且没有错误,请执行Process3()
SuccessCondition()
ErrorCondition()
我的理解是,我将创建一个Task
并调用ContinueWith()
来链接更多任务,传入TaskContinuationOptions
标志以确定何时执行该延续。此外,成功/错误条件将贯穿所有延续到最后。所以我现在正在尝试这个:
var task = new Task(() => Process1());
var task2 = task.ContinueWith(t => Process2(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task3 = task2.ContinueWith(t => Process3(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task4 = task3.ContinueWith(t => SuccessCondition(t), TaskContinuationOptions.OnlyOnRanToCompletion);
var task5 = task4.ContinueWith(t => ErrorCondition(t), TaskContinuationOptions.NotOnRanToCompletion);
task.Start();
它似乎表现得像预期的那样,除了在ErrorCondition()
内,t
的实例似乎没有例外,即使我手动从内部抛出一个,例如{{1} }}。查看用于处理异常的MSDN文章,它说:
Process2()
然而,我试过了,似乎也没有例外。另外,通过在主线程中调用try
{
task.Wait();
}
catch (AggregateException ex)
{
// Handle exceptions from a collection on ex
}
,我是否可以否定并行性并阻止?在我的测试中看起来就像这样。
所以我想我的问题是......链接相关任务和处理整体成功/错误情况的正确方法是什么?或者,如何正确捕获从这些任务中抛出的异常,同时仍然立即返回到UI?
答案 0 :(得分:3)
您需要为所有任务1-4添加一个延续,并使用错误处理案例允许任何一个错误调用该函数。
为方便起见,您可以创建一个方法,将相同的延续添加到任务集合中。这是一个(根据需要随意添加其他重载的ContinueWith):
public static IEnumerable<Task> ContinueWith(this IEnumerable<Task> tasks
, Action<Task> continuation, TaskContinuationOptions options)
{
return tasks.Select(task => task.ContinueWith(continuation, options))
.ToList();//important for this ToList to be here;
//we want the continuations to be added now, not when the result is iterated
}
这允许你写:
var errorTasks = new[]{task, task2, task3, task4}
.ContinueWith(ErrorCondition, TaskContinuationOptions.NotOnRanToCompletion);
使用async
方法在C#5.0中使任务的错误处理变得更加容易。它允许您将代码转换为:
public static async Task Foo()
{
try
{
await Task.Run(Process1())
await Task.Run(Process2())
await Task.Run(Process3())
SuccessCondition();
}
catch (SomeExceptionType ex)
{
HandleException(ex);
}
}
这就像您认为的那样,按照您的要求运行,这很棒。
答案 1 :(得分:3)
请注意,如果您使用的是.NET 4.5并且可以使用async / await,那么您可以更干净地执行此操作:
public async Task DoProcess()
{
try
{
await Task.Run(Process1);
await Task.Run(Process2);
await Task.Run(Process3);
SuccessCondition();
}
catch (Exception ex)
{
ErrorCondition(ex);
}
}
如果从UI线程启动它,则还会在UI线程上发生SuccessCondition和ErrorCondition。这里的一个功能区别是你捕获的异常不会是AggregateException;它将是在等待的呼叫失败期间抛出的实际异常。