在foreach循环中使用parallel.foreach和task之间的性能差异是什么

本文关键字:foreach 之间 性能 是什么 task 循环 parallel | 更新日期: 2023-09-27 18:27:14

我想知道什么是最好的方法,或者有没有任何文档/文章可以帮助我确定在每个循环,如下所示:

案例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?

在foreach循环中使用parallel.foreach和task之间的性能差异是什么

首先,关于这个主题的最佳文档是CLR的第五部分(通过C#)。

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.ForEach()所做的是创建少量的Task来处理循环的迭代。Task相对便宜,但它们不是免费的,因此这往往会提高性能。而且循环的主体执行得很快,改进可能真的很大。这是对你所观察到的行为最可能的解释。

您正在运行多少任务?如果循环足够多的话,仅仅创建一个新任务就可能需要大量的时间。即,对于第一个块,以下运行时间为15ms,对于第二个块,运行时间超过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);
        }
    }
}