【问题标题】:PLINQ: how to run a ParallelQuery on more than 4 threads?PLINQ:如何在超过 4 个线程上运行 ParallelQuery?
【发布时间】:2011-07-27 11:51:11
【问题描述】:

更新 - 更改了问题的标题以反映我真正追求的目标

考虑以下代码:

// 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());

【问题讨论】:

  • WithDegreeOfParallelism 仅设置上限。 Plinq 将选择低于该数字的最佳并行度
  • 好的,但它怎么知道 4 是这种情况下的最佳值?有没有办法强制使用 PLINQ 在 12 个并发线程上运行它?
  • 实际上最适合您的是 3。为什么要运行的线程数多于内核数?只是开销增加。
  • @Henk 你能详细说明为什么不应该用 Thread.Sleep 模拟吗?
  • 你应该在这里使用异步代码,而不是像这样捆绑(线程池)线程。 P/LINQ 不是万能的框架,它适用于 CPU 密集型操作,但在这种情况下,您应该在 Web 请求上使用 BeginXYZ 操作之类的东西。这样你就不会束缚线程。

标签: c# plinq


【解决方案1】:

尝试使用选项TaskCreationOptions.LongRunning 在并行循环中启动新任务。它们会立即启动,而不是等到线程池中的线程可用。

【讨论】:

    【解决方案2】:

    正如我所说,WithDegreeOfParallelism 只设置了一个上限。尝试将您的任务从 10 增加到 100。您最终将获得大约 10 秒的所有 100 秒。您的代码适用于具有较小操作的大量任务。 并在你的任务中添加Console.WriteLine("{0} threads " ,Process.GetCurrentProcess().Threads.Count); 然后你可以看到创建了多少线程。(线程数不是 plinq 创建的线程数。看看它是如何增加的)。

    有很多方法可以使用 PLinq 进行并行处理。阅读这篇文章http://msdn.microsoft.com/en-us/library/dd997411.aspx。您需要为相关要求选择最佳方式以获得更好的性能。

    【讨论】:

      【解决方案3】:

      WithDegreeOfParallelism 规定了 PLINQ 应该创建多少个任务,但不一定要使用多少个线程。

      由于任务作为工作项在 ThreadPool 上执行,因此执行查询的线程数将受到 ThreadPool 大小的限制。 ThreadPool 会根据需要添加线程,但可能需要一段时间 - ThreadPool 每秒可能会添加 2 个线程左右。

      如果想快速将线程添加到 ThreadPool 中,可以使用 SetMinThreads 方法。如果你把这段代码放在代码的开头,测试应该在一秒钟左右完成:

      int prevThreads, prevPorts;
      ThreadPool.GetMinThreads(out prevThreads, out prevPorts);
      ThreadPool.SetMinThreads(12, prevPorts);
      

      您可以决定需要多少线程,然后使用 SetMinThreads 和 SetMaxThreads 设置 ThreadPool 大小的界限。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多