使用TPL进行任务组合和错误处理

7

我有一个具有以下结构的方法:

public Task InitializeAsync()
{
    var taskCompletionSource = new TaskCompletionSource<bool>();

    Task firstTask = ...;

    // secondTask calls taskCompletionSource.TrySetResult(true) once it considers itself "done"
    Task secondTask = firstTask.ContinueWith(..., TaskContinuationOptions.OnlyOnRanToCompletion);

    Action<TasK> errorContinuation = x =>
        {
            taskCompletionSource.SetException(e.Exception);
        };

    firstTask.ContinueWith(errorContinuation, TaskContinuationOptions.OnlyOnFaulted);
    secondTask.ContinueWith(errorContinuation, TaskContinuationOptions.OnlyOnFaulted);

    return taskCompletionSource.Task;
}

重要提示:

  • InitializeAsync 返回的任务直到 secondTask 决定结束才算完成
  • 只有当 firstTask 成功时,secondTask 才会运行
  • 无论是 firstTask 还是 secondTask 失败都会导致整个任务失败

我在想是否有更简洁、更简单的方式来表达这个功能。我正在使用 .NET 4.0,但也想知道 4.5 是否能使此过程更加容易。


1
在4.5版本中,您可以通过await/async特性和try/catch来将任务插入到异步函数中。 - Random Dev
最近我也遇到了相同的问题,并且走了与Gideon回答中所链接的Stephen Toub的博客文章相同的路线。这种方法更加简洁明了,而且显然可以处理所有边缘情况,要比我最初尝试的解决方案更好。 - anton.burger
2个回答

5
对于 .NET 4.0,我使用了来自这篇博客文章的一个想法来像您描述的那样链接任务。 特别是,请查看标题为Then的部分。 注意,他的版本希望您传递返回任务的函数,而不仅仅是像传递给ContinueWith一样传递方法。
顺便提一下,Then让你很接近通过LINQ from子句链接任务所需的SelectMany。 尽管在 .NET 4.5 中有async/await,但我主要提到这个语法选项,虽然我自己并没有使用它。

这是一个干净、通用的解决方案,我很惊讶它不在BCL中,尽管TaskCompletionSource使其实现变得非常简单。如果你想要一个更像ContinueWith的签名,你需要提供重载来指定取消令牌、调度程序等。 - anton.burger
1
这正是我理解上需要填补的漏洞。非常感谢。 - Kent Boogaart

0
另一个选择是将您的两个任务创建为附加子任务(嵌套在返回的父任务中)。
只有当附加的子任务完成时,父任务才会完成。 子任务的错误被视为父任务的错误。

http://msdn.microsoft.com/en-us/library/dd997417.aspx


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