我对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调用拉入原始任务吗?它仍将以异步方式运行。必须有一些我不理解的东西。
答案 0 :(得分:3)
您假设每个Task
只是一个在线程池线程中运行的同步方法。事实并非如此,你不应该以这种方式思考任务。 Task
是某种在某些时候完成的工作,可能会产生一个值。它可能是在线程池线程中运行一个方法,它可能正在等待事件触发,它可能正在等待对网络请求的响应,或者您的硬盘驱动器完成将一些数据写入文件。
是的,如果您的Task
特别是在线程池线程中运行某些同步代码,并且您总是希望在同一个线程池之后立即运行更多同步代码线程,你是控制创建Task
的人,而其他任何东西都不需要表示中间结果的Task
,那么你可以改变用于生成{{1}的方法。 1}}正如你所展示的那样(如果你处于这种情况,你最好这样做)。这最终成为一个相当狭窄的案例。
答案 1 :(得分:1)
正如我在博客中描述的那样,there is exactly one use case for ContinueWith
:dynamic task parallelism。
不应该用于:'
“动态任务并行”是指当你想要使用多个线程进行一系列CPU绑定工作时(“并行性”),并将工作划分为多个CPU绑定任务(“任务并行”) ),和你不知道在处理它之前你需要完成多少任务(“动态任务并行”)。
换句话说,您几乎不应该使用ContinueWith
。
答案 2 :(得分:0)
对于同步代码,调用.Result
是一个阻塞操作 - 特别是当您知道结果已经存在时。使用ContinueWith
为安排回调结果可用 - async 代码(尽管不是通过async
/ await
模式)。请注意,如果结果已经可用,则会直接将调用重新调用到调用线程。
您现有的代码很糟糕,可能导致致命的死锁。不要这样做:)在这种情况下,你正在躲过它 ,但是在任何事情中都会有很多陷阱,并且会通过异步同步来实现#34; (又名&#34;调用.Wait()
或访问.Result
)。