对于ContinueWith的工作方式我很困惑,它似乎阻止了ThreadPool并按顺序运行任务。以下面我写的代码示例:为什么ContinueWith显示为顺序阻塞和/或正在运行任务
var items = new List<int>();
for (int i = 0; i < 100; i++)
{
items.Add(i);
}
Parallel.ForEach(items, item =>
{
Task.Factory.StartNew(() =>
{
}).ContinueWith(t =>
{
if (item < 50)
{
System.Console.WriteLine("Blocking in {0}", item.ToString());
var x = SomeLongRunningDatabaseCall(10001);
}
System.Console.WriteLine("Item {0}", item);
});
});
该代码的目的是为了镜像我在生产应用程序中遇到的可疑线程阻塞问题。令我惊讶的是,我发现这个问题似乎与我使用ContinueWith相关。有趣的是,如果我在ContinueWith上设置了TaskContinuationOptions.LongRunning选项,它可以在不阻塞的情况下异步运行这些任务,我也在我的生产应用程序中完成了这项工作,并解决了这个问题。
但是,我真的很困惑,并且想更好地理解为什么没有选项TaskContinuationOptions.LongRunning的ContinueWith语句会导致任务阻塞或似乎按顺序运行,尽管我每次迭代都调用一个新线程。我能想到的唯一的事情是,ContinueWith没有在执行前面任务的线程中运行,而是在主线程中执行,这可能会导致该块。
任何帮助或建议将不胜感激。
更新
而且有趣的是,如果我取代
SomeLongRunningDatabaseCall(10001)
的东西,如
Thread.Sleep(600000)
但是,它没有用它做数据库,但由于呼叫阻塞它运行在它自己的线程中,至少对其他任务不应该有任何阻塞,因为任务> 50不会调用数据库。
对'ContinueWith'的调用几乎肯定不会被阻止。实际操作可能会顺序运行;如果他们是,这是由于他们正在做的事情的性质,而不是'ContinueWith'是如何实现的。这就是为什么你看到“睡眠”呼叫被并行化的原因。 – Servy 2014-12-03 18:44:37
@Servy啊,我明白你的意思了。你如何解释这样一个事实,即任务> 50在其他任务完成之前不会运行,因为它们应该在不同的线程上运行。 (我不知道你是否有机会看到我的更新) – 2014-12-03 18:55:12
这只是说明一些任务似乎在等待其他任务。你不能有无限的平行度。我并不感到惊讶,因为你不能有超过50个并行的数据库调用,我也不希望有更多的并行运行带来性能上的好处。 – Servy 2014-12-03 19:00:37