更新 - 更改了问题的标题以反映我真正追求的内容
考虑以下代码:
// this query generates 12 instances of Func<int>, which each when executed
// print something to the console and wait for 1 second.
var actions = Enumerable.Range(0, 12).Select(i => new Func<int>(() =>
{
Console.WriteLine("{0} - waiting 1 sec", i);
Thread.Sleep(1000);
return 1;
}));
// define a parallel query. Note the WithDegreeOfParallelism call here.
var query = from action in actions.AsParallel().WithDegreeOfParallelism(12)
select action();
// execute, measuring total duration
var stopw = Stopwatch.StartNew();
query.ToList();
Console.WriteLine(stopw.Elapsed);
Console.WriteLine(Environment.ProcessorCount); // 3 on my machine
当忽略对WithDegreeOfParallelism
的调用时,这将在4个块中执行,总共花费大约4秒,这是我期望的,因为我的CPU数量是3。
但是,当用{4}以上的任何数字调用WithDegreeOfParallelism
时,我总是得到3个块,总持续时间不会低于3秒。我希望12的值总持续时间(略大于)1秒。
我错过了什么?我如何强制执行超过4个非CPU密集型任务的并行执行,这就是我所追求的?
更新:我当然可以回去手动旋转线程,但我希望新的PFX库能让这更容易......无论如何,下面的代码给了我大约1秒的总执行时间
List<Thread> threads = new List<Thread>();
for (int i = 0; i < 12; i++)
{
int i1 = i;
threads.Add(new Thread(() =>
{
Console.WriteLine(i1);
Thread.Sleep(1000);
}));
}
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
答案 0 :(得分:3)
尝试使用选项TaskCreationOptions.LongRunning
在并行循环中启动新任务。它们将立即启动,而不是等到线程池上的线程可用。
答案 1 :(得分:2)
正如我所说,WithDegreeOfParallelism只设置一个上限。尝试将你的任务从10增加到100.你将最终为所有100个任务增加约10个。您的代码适用于具有较小操作的大量任务。
并在你的任务中添加Console.WriteLine("{0} threads " ,Process.GetCurrentProcess().Threads.Count);
然后你可以看到创建了多少个线程。(线程数不是plinq创建的线程的数量。看看它是如何增加的。)
有很多方法可以与PLinq进行并行处理。阅读这篇文章http://msdn.microsoft.com/en-us/library/dd997411.aspx。您需要为相关要求选择最佳方式以获得更好的性能。
答案 2 :(得分:2)
WithDegreeOfParallelism指示PLINQ应创建多少个任务,但不一定要使用多少个线程。
由于Tasks在ThreadPool上作为工作项执行,因此执行查询的线程数将受限于ThreadPool的大小。 ThreadPool将根据需要添加线程,但可能需要一段时间 - ThreadPool可能每秒添加2个线程。
如果要快速向ThreadPool添加线程,可以使用SetMinThreads方法。如果您将此代码放在代码的开头,那么测试应该在一秒左右完成:
int prevThreads, prevPorts;
ThreadPool.GetMinThreads(out prevThreads, out prevPorts);
ThreadPool.SetMinThreads(12, prevPorts);
您可以决定需要多少线程,然后使用SetMinThreads和SetMaxThreads设置ThreadPool大小的边界。