【问题标题】:What is the performance differences between using parallel.foreach and task inside foreach loop?在 foreach 循环中使用 parallel.foreach 和 task 之间的性能差异是什么?
【发布时间】:2013-08-12 09:37:17
【问题描述】:

我想知道什么是最好的方法,或者是否有任何文档/文章可以帮助我确定在正常情况下使用 Parallel.foreach 和 Task 的区别是什么 每个循环,如下所示:

案例 1 - Parallel.foreach:

Parallel.foreach
{
  // Do SOmething thread safe: parsing an xml and then save 
  // into a DB Server thry respoitory approach
}

案例 2 - foreach 中的任务:

foreach
{
  Task t1 = Task.factory.startNew(()=>
  {
     //Do the same thing as case 1 that is thread safe
  }
}
Task.waitall()
  • 我做了自己的测试,结果显示案例 1 的性能比案例 2 好。比率大约是这样的: 顺序 vs 案例 1 vs 案例 2 = 5s : 1s : 4s

虽然案例 1 和案例 2 几乎是 1:4?那么这是否意味着如果我们想在循环中并行运行,我们应该始终使用 parallel.foreach 或 parallel.for?

【问题讨论】:

  • 男孩,我不相信你的测试结果吗...这些数字应该给你敲响警钟。
  • @Will - 创建任务与创建线程非常不同。 TPL 的存在理由
  • @威尔,谢谢你的输入,我想我有同样的想法,但为什么结果显示不同?还有4次……
  • 我不明白,你好像在说情况1的4s比情况2的1s好。
  • 请原谅我糟糕的英语 + 在这里发布问题时缺乏经验。我要说的是,案例 1 只需要 1 秒来完成每个循环的工作,而案例 2 需要大约 4 秒来完成每个循环。 [编辑问题以解决混乱]​​pan>

标签: c# task-parallel-library parallel.foreach


【解决方案1】:

首先,关于该主题的最佳文档是 C# 的 CLR 的第五部分。

http://www.amazon.com/CLR-via-C-Developer-Reference/dp/0735667454/ref=sr_1_1?ie=UTF8&qid=1376239791&sr=8-1&keywords=clr+via+c%23

其次,我希望 Parallel.Foreach 表现更好,因为它不仅会创建任务,还会对它们进行分组。在 Jeffrey Richter 的书中,他解释说,单独启动的任务将被放入线程池队列中。锁定实际线程池队列有一些开销。为了解决这个问题,任务本身有自己创建的任务队列。 Tasks持有的这个任务子队列其实可以不加锁地做一些工作!

我将不得不再次阅读该章(第 27 章),因此我不确定 Parallel.Foreach 是否以这种方式工作,但这是我期望的。

他解释说,锁定很昂贵,因为它需要访问内核级别的结构。

在任何一种情况下,都不要期望它们按顺序处理。由于上述内部结构,使用 Parallel.Foreach 比 foreach 关键字更不可能按顺序处理。

【讨论】:

  • 我还没有开始阅读这本书,但我同意您关于parallel.each 中的“组”功能的观点。在我的parallel.each 中,我确实希望有一个块是关键部分,但当我以parallel.foreach 方式实现它时,结果却不是。我想在读完本章后发表更多评论。感谢您的推荐,这本书看起来很有希望加强我的知识。
  • 我不认为他在Parallel的上下文中直接谈到了Task队列机制,但不是在那一章里,就是在下一章里。
【解决方案2】:

Parallel.ForEach() 的作用是创建少量Tasks 来处理循环的迭代。 Tasks 相对便宜,但它们不是免费的,所以这往往会提高性能。而且你的循环体执行很快,改进可能非常大。这是您观察到的行为的最可能的解释。

【讨论】:

  • 我不确定我是否理解最后两句话。关于“循环主体”,您在谈论哪种实现?并行.foreach?还是 foreach 循环中的任务?
  • @mting923 我的意思是循环的一次迭代,而不是整个Task
【解决方案3】:

您正在运行多少任务?如果循环足够多,仅创建新任务可能需要大量时间。即,第一个块的以下运行时间为 15 毫秒,第二个块的运行时间超过 1 秒,第二个块甚至不运行任务。取消注释Start,时间会上升到近 3 秒。 WaitAll 只添加了少量。

static class Program
{
    static void Main()
    {
        const int max = 3000000;
        var range = Enumerable.Range(0, max).ToArray();
        {
            var sw = new Stopwatch();
            sw.Start();
            Parallel.ForEach(range, i => { });
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
        {
            var tasks = new Task[max];
            var sw = new Stopwatch();
            sw.Start();
            foreach (var i in range)
            {
                tasks[i] = new Task(()=> { });
                //tasks[i].Start();
            }
            //Task.WaitAll(tasks);
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
}

【讨论】:

  • 感谢您的示例代码,我会在输入之前尝试一下。但我也会得到每个循环的平均时间来比较。
猜你喜欢
  • 1970-01-01
  • 2010-09-20
  • 2015-07-05
  • 1970-01-01
  • 1970-01-01
  • 2016-03-22
  • 2016-08-03
  • 2018-01-09
相关资源
最近更新 更多