TPL ContinueWith方法的目的是什么?

时间:2016-09-12 20:06:41

标签: c# task-parallel-library continuations

我对TPL ContinueWith方法感到困惑。我不明白为什么需要它。 Here's an example from MSDN显示了如何使用ContinueWith

static void SimpleContinuationWithState()
{
   int[] nums = { 19, 17, 21, 4, 13, 8, 12, 7, 3, 5 };
   var f0 = new Task<double>(() => nums.Average());
   var f1 = f0.ContinueWith(t => GetStandardDeviation(nums, t.Result));

   f0.Start();
   Console.WriteLine("the standard deviation is {0}", f1.Result);
}

似乎我可以删除ContinueWith来电而不改变结果:

static void SimpleContinuationWithState()
{
   int[] nums = { 19, 17, 21, 4, 13, 8, 12, 7, 3, 5 };
   var f0 = new Task<double>(() => GetStandardDeviation(nums, nums.Average()));

   f0.Start();
   Console.WriteLine("the standard deviation is {0}", f0.Result);
}

这个标准偏差的例子必须是一个人为的例子,但我想不出使用ContinueWith的理由。 (除非某些库调用创建了Task而不是我)在每种情况下,我都不能将ContinueWith调用拉入原始任务吗?它仍将以异步方式运行。必须有一些我不理解的东西。

3 个答案:

答案 0 :(得分:3)

您假设每个Task只是一个在线程池线程中运行的同步方法。事实并非如此,你不应该以这种方式思考任务。 Task是某种在某些时候完成的工作,可能会产生一个值。它可能是在线程池线程中运行一个方法,它可能正在等待事件触发,它可能正在等待对网络请求的响应,或者您的硬盘驱动器完成将一些数据写入文件。

是的,如果您的Task特别是在线程池线程中运行某些同步代码,并且您总是希望在同一个线程池之后立即运行更多同步代码线程,你是控制创建Task的人,而其他任何东西都不需要表示中间结果的Task,那么你可以改变用于生成{{1}的方法。 1}}正如你所展示的那样(如果你处于这种情况,你最好这样做)。这最终成为一个相当狭窄的案例。

答案 1 :(得分:1)

正如我在博客中描述的那样,there is exactly one use case for ContinueWithdynamic task parallelism

不应该用于:'

  • 异步代码。
  • 数据并行。
  • 静态任务并行。

“动态任务并行”是指当你想要使用多个线程进行一系列CPU绑定工作时(“并行性”),并将工作划分为多个CPU绑定任务(“任务并行”) ),你不知道在处理它之前你需要完成多少任务(“动态任务并行”)。

换句话说,您几乎不应该使用ContinueWith

答案 2 :(得分:0)

对于同步代码,调用.Result是一个阻塞操作 - 特别是当您知道结果已经存在时。使用ContinueWith安排回调结果可用 - async 代码(尽管不是通过async / await模式)。请注意,如果结果已经可用,则会直接将调用重新调用到调用线程。

您现有的代码很糟糕,可能导致致命的死锁。不要这样做:)在这种情况下,你正在躲过它 ,但是在任何事情中都会有很多陷阱,并且会通过异步同步来实现#34; (又名&#34;调用.Wait()或访问.Result)。

相关问题