我正在尝试这段代码(只产生一些任务并模拟工作):
var tasks = Enumerable.Range(1, 10).Select(d => Task.Factory.StartNew(() =>
{
Console.Out.WriteLine("Processing [{0}]", d);
Task.Delay(20000).Wait(); // Simulate work. Here will be some web service calls taking 7/8+ seconds.
Console.Out.WriteLine("Task Complete [{0}]", d);
return (2 * d).ToString();
})).ToList();
var results = Task.WhenAll(tasks).Result;
Console.Out.WriteLine("All processing were complete with results: {0}", string.Join("|", results));
我原本希望在控制台中同时看到10个Processing ...
;但是当我跑步时,我最初看到了这个输出
Processing [1]
Processing [2]
Processing [3]
Processing [4]
然后半秒后Processing [5]
,Processing [6]
和其他人一个接一个地缓慢显示。
你能解释一下吗?这是否意味着任务正在延迟启动?为什么呢?
答案 0 :(得分:3)
如另一个答案中所述,使用TaskCreationOptions.LongRunning
将解决您的问题。
但这不是你应该如何处理你的问题。您的示例模拟 CPU绑定工作。您说您的任务将调用Web服务 - 这意味着它们将 IO绑定。
因此,它们应该以异步方式运行 。但是,Task.Delay(20000).Wait();
等待同步,因此它不代表将要/应该实际发生的事情。
改为举例:
var tasks = Enumerable.Range(1, 10).Select(async d =>
{
Console.Out.WriteLine("Processing [{0}]", d);
await Task.Delay(5000); // Simulate IO work. Here will be some web service calls taking 7/8+ seconds.
Console.Out.WriteLine("Task Complete [{0}]", d);
return (2*d).ToString();
}).ToList();
var results = Task.WhenAll(tasks).Result;
Console.Out.WriteLine("All processing were complete with results: {0}", string.Join("|", results));
所有任务都按预期立即启动。
答案 1 :(得分:1)
我希望你有4个cpu核心。
让两个(cpu bond)线程争夺核心导致它需要更长时间才能完成工作,然后让1个线程执行第一个任务,然后执行第二个任务。
除非另有说明,否则任务系统会假定任务运行时间短且CPU受限,并且他们将使用“无阻塞”IO。
因此,我希望任务系统默认为接近核心数的线程数。
使用TaskCreationOptions.LongRunning
向TaskScheduler提供了超额订阅的提示 必要的。 Oversubscription允许您创建更多的线程 可用的硬件线程数。
最后任务不是线程,它们的设计是隐藏线程的大量细节,包括控制正在使用的线程数。创建100s是合理的任务,如果你创建的100个线程都试图同时运行,那么cpu cache等将会非常困难。
然而,让我们回到你想要做的事情。您的示例模拟CPU绑定工作。您说您的任务将调用Web服务 - 这意味着它们将受IO限制。
因此,它们应该异步运行。但是,Task.Delay(20000).Wait();
会同步等待,因此它不代表将要/应该实际发生的事情。有关使用await
进行异步延迟的代码示例,请参阅Gediminas Masaitis答案。但是,只要您使用更多异步代码,就需要更多地考虑锁定等问题。
如果同时发出100个请求,异步IO显然会更好。但是,如果您的应用程序中只有“手满”并且没有使用await
,那么TaskCreationOptions.LongRunning
可能就足够了。