是否建议将prevTask.Wait()与ContinueWith一起使用(来自Tasks库)?

时间:2012-08-10 15:29:27

标签: c# task-parallel-library

所以我最近被告知我如何使用我的.ContinueWith for Tasks并不是使用它们的正确方法。我还没有在互联网上找到这方面的证据,所以我会问你们,看看答案是什么。以下是我如何使用.ContinueWith:

的示例
public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

现在我知道这是一个简单的例子,它运行得非常快,但只是假设每个任务都进行了一些更长的操作。所以,我被告知在.ContinueWith中,你需要说prevTask.Wait();否则你可以在上一个任务完成之前完成工作。这甚至可能吗?我假设我的第二个&第三个任务只有在前一个任务完成后才会运行。

我被告知如何编写代码:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

7 个答案:

答案 0 :(得分:116)

嗯......我认为目前的一些答案缺少一些东西:例外会发生什么?

在延续中调用Wait的唯一原因是观察延续本身中前因的潜在异常。如果您在Result的情况下访问Task<T>,并且手动访问Exception属性,则会发生相同的观察。 坦率地说,我不会打电话给Wait或访问Result,因为如果有例外,您将支付重新加价的费用,这是不必要的开销。相反,您只需检查先行IsFaulted下的Task属性即可。或者,您可以通过链接多个同级延续来创建分叉工作流,这些延续只会基于TaskContinuationOptions.OnlyOnRanToCompletionTaskContinuationOptions.OnlyOnFaulted的成功或失败而触发。

现在,没有必要在延续中观察前提的例外情况,但如果“步骤1”失败,您可能不希望工作流程继续前进。在这种情况下:为TaskContinuationOptions.NotOnFaulted调用指定ContinueWith会阻止连续逻辑永远触发。

请记住,如果您自己的延续没有观察到异常,那么等待整个工作流程完成的人将成为观察它的人。要么Wait Task在{{1}}上游,要么已经自行延续,以了解它何时完成。如果是后者,他们的继续需要使用上述观察逻辑。

答案 1 :(得分:20)

您正确使用它。

  

创建一个在目标时异步执行的延续   任务完成。

来源:Task.ContinueWith Method (Action as MSDN)

在每个prevTask.Wait()调用中调用Task.ContinueWith似乎是一种奇怪的方式来重复不必要的逻辑 - 即做一些“超级确定”,因为你实际上不明白什么是一点点代码呢。就像检查一个null一样只是为了抛出ArgumentNullException无论如何都会抛出它。

所以,不,谁告诉你这是错的,可能不明白为什么Task.ContinueWith存在。

答案 2 :(得分:16)

谁告诉过你的?

引用MSDN

  

创建在目标时异步执行的延续   任务完成。

此外,如果没有等待上一个任务完成,继续的目的是什么?

您甚至可以自己测试一下:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

答案 3 :(得分:5)

Task.Continuewith上的MSDN

  

返回的任务将不会被安排执行,直到   当前任务已完成。如果通过指定的标准   continuationOptions参数不符合,延续任务将   取消而不是预定。

我认为你希望它在第一个例子中起作用的方式是正确的方法。

答案 4 :(得分:1)

您可能还想考虑使用Task.Run而不是Task.Factory.StartNew。

Stephen Cleary的blog post和Stephen Toub的post that he references解释了这些差异。在this answer中也有讨论。

答案 5 :(得分:0)

通过访问Task.Result,您实际上正在执行与task.wait

类似的逻辑

答案 6 :(得分:0)

我将重申许多人已经说过的话,prevTask.Wait() 是不必要的

有关更多示例,可以转到Chaining Tasks using Continuation Tasks,另一个链接由Microsoft提供了很好的示例。