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

问题描述 投票:88回答:7

所以我最近被告知我如何使用我的.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");
    });
}
c# task-parallel-library
7个回答
116
投票

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

你继续称之为Wait的唯一原因是观察延续本身中前因的潜在例外。如果你在Result的情况下访问Task<T>,并且你手动访问Exception属性,也会发生同样的观察。坦率地说,我不会打电话给Wait或访问Result,因为如果有例外,你将支付重新筹集它的价格,这是不必要的开销。相反,你可以检查先前IsFaultedTask属性。或者,您可以通过链接多个兄弟连续来创建分叉工作流,这些连续仅基于TaskContinuationOptions.OnlyOnRanToCompletionTaskContinuationOptions.OnlyOnFaulted的成功或失败而触发。

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

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


20
投票

您正确使用它。

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

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

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

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


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");
    });

5
投票

来自MSDNTask.Continuewith

在当前任务完成之前,将不会安排返回的任务执行。如果不满足通过continuationOptions参数指定的条件,则将取消继续任务而不是已安排。

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


1
投票

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

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


0
投票

通过访问Task.Result,你实际上正在做与task.wait类似的逻辑


0
投票

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

有关更多示例,可以访问Chaining Tasks using Continuation Tasks,这是Microsoft提供的另一个链接示例。

© www.soinside.com 2019 - 2024. All rights reserved.