必威体育Betway必威体育官网
当前位置:首页 > IT技术

C# 多线程 Parallel.ForEach 和 ForEach 效率问题研究及理解

时间:2019-09-26 04:15:35来源:IT技术作者:seo实验室小编阅读:55次「手机版」
 

parallel.foreach

最近要做一个数据dataTable循环操作,开始发现 运用foreach,进行大数据循环,并做了一些逻辑处理。在循环中耗费的时间过长。后来换成使用parallel.ForEach来进行循环。 一开始认为, 数据比较大时,Parallel.ForEach肯定比 ForEach效率高,后来发现,其实并不是这样。

我用了1000万次循环测试:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace consoleAPPlication1
{
    class Program
    {
        static void Main(string[] args)
        {
         
            Stopwatch Watch1 = new Stopwatch();
            Watch1.Start();
            List<entityA> source = new List<entityA>();
            for (int i = 0; i < 10000000; i++)
            {
                source.Add(new entityA
                {
                    name = "悟空" + i,
                    sex = i % 2 == 0 ? "男" : "女",
                    age = i
                });
            }
            Watch1.Stop();

            Console.WriteLine("list循环插入耗时:" + Watch1.ElapsedMilliseconds);
            Stopwatch Watch2 = new Stopwatch();
            Watch2.Start();
            loop1(source);
            Watch2.Stop();
            Console.WriteLine("一般for循环耗时:" + Watch2.ElapsedMilliseconds);

            Stopwatch Watch3 = new Stopwatch();
            Watch3.Start();
            loop2(source);
            Watch3.Stop();
            Console.WriteLine("一般foreach循环耗时:" + Watch3.ElapsedMilliseconds);


            Stopwatch Watch4 = new Stopwatch();
            Watch4.Start();
            loop3(source);
            Watch4.Stop();
            Console.WriteLine("并行for循环耗时:" + Watch4.ElapsedMilliseconds);

            Stopwatch Watch5 = new Stopwatch();
            Watch5.Start();
            loop4(source);
            Watch5.Stop();
            Console.WriteLine("并行foreach循环耗时:" + Watch5.ElapsedMilliseconds);
            Console.ReadLine();
        }
        //普通的for循环
        static void loop1(List<entityA> source)
        {
            int count = source.Count();
            for (int i = 0; i < count; i++)
            {
                source[0].age= + 10;
                //System.Threading.Thread.Sleep(10);
            }
        }

        //普通的foreach循环
        static void loop2(List<entityA> source)
        {
            foreach (entityA item in source)
            {
                item.age =+ 10;
                //System.Threading.Thread.Sleep(10);
            }
        }

        //并行的for循环
        static void loop3(List<entityA> source)
        {
            int count = source.Count();
            Parallel.For(0, count, item =>
            {
                //source[count].age= source[count].age + 10;
                //System.Threading.Thread.Sleep(10);
            });
        }

        //并行的foreach循环
        static void loop4(List<entityA> source)
        {
            Parallel.ForEach(source, item =>
            {
                item.age = item.age + 10;
                //System.Threading.Thread.Sleep(10);
            });
        }
    }


    //简单的实体
    class entityA
    {
        public string name { set; get; }
        public string sex { set; get; }
        public int age { set; get; }
    }
}

运行结果:

结果居然是并行比一般的循环还耗时,但这是为什么呢?

这是因为循环体内执行的任务开销太小,仅仅是age+10 而已。微软文章已经指出任务的开销大小对并行任务的影响。如果任务很小,那么由于并行管理的附加开销(任务分配,调度,同步等成本),可能并行执行并不是优化方案。这也是上述程序Foreach与For效率高出的原因。

基于这一点,我们对程序进行调整,循环1000次,每次里面线程sleep(10),这样我们试试。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
         
            Stopwatch Watch1 = new Stopwatch();
            Watch1.Start();
            List<entityA> source = new List<entityA>();
            for (int i = 0; i < 1000; i++)
            {
                source.Add(new entityA
                {
                    name = "悟空" + i,
                    sex = i % 2 == 0 ? "男" : "女",
                    age = i
                });
            }
            Watch1.Stop();

            Console.WriteLine("list循环插入耗时:" + Watch1.ElapsedMilliseconds);
            Stopwatch Watch2 = new Stopwatch();
            Watch2.Start();
            loop1(source);
            Watch2.Stop();
            Console.WriteLine("一般for循环耗时:" + Watch2.ElapsedMilliseconds);

            Stopwatch Watch3 = new Stopwatch();
            Watch3.Start();
            loop2(source);
            Watch3.Stop();
            Console.WriteLine("一般foreach循环耗时:" + Watch3.ElapsedMilliseconds);


            Stopwatch Watch4 = new Stopwatch();
            Watch4.Start();
            loop3(source);
            Watch4.Stop();
            Console.WriteLine("并行for循环耗时:" + Watch4.ElapsedMilliseconds);

            Stopwatch Watch5 = new Stopwatch();
            Watch5.Start();
            loop4(source);
            Watch5.Stop();
            Console.WriteLine("并行foreach循环耗时:" + Watch5.ElapsedMilliseconds);
            Console.ReadLine();
        }
        //普通的for循环
        static void loop1(List<entityA> source)
        {
            int count = source.Count();
            for (int i = 0; i < count; i++)
            {
                source[0].age= + 10;
                System.Threading.Thread.Sleep(10);
            }
        }

        //普通的foreach循环
        static void loop2(List<entityA> source)
        {
            foreach (entityA item in source)
            {
                item.age =+ 10;
                System.Threading.Thread.Sleep(10);
            }
        }

        //并行的for循环
        static void loop3(List<entityA> source)
        {
            int count = source.Count();
            Parallel.For(0, count, item =>
            {
                //source[count].age= source[count].age + 10;
                System.Threading.Thread.Sleep(10);
            });
        }

        //并行的foreach循环
        static void loop4(List<entityA> source)
        {
            Parallel.ForEach(source, item =>
            {
                item.age = item.age + 10;
                System.Threading.Thread.Sleep(10);
            });
        }
    }


    //简单的实体
    class entityA
    {
        public string name { set; get; }
        public string sex { set; get; }
        public int age { set; get; }
    }
}

执行结果:

效率一目了然。

这样的结果认证了我们上面的结论。当我们在循环中执行时间过长时,我们需要采用并行循环,效率较高。当时间过短,我们需要用foreach和for.

相关阅读

Parallels Desktop 13下载安装虚拟机使用教程

Parallels Desktop 可在Windows 与Mac OS 应用程序之间随意拖放文件并直接从Mac dock 启动Windows 程序,能够在Mac上以最便捷、快

ForEach

一边遍历list 可以用for 或者foreach去操作,后来发现list本身就有迭代的方法,ForEach查看MSDN的介绍:ForEach 本身要传一个Action的

Parallel()

Joblib提供了一个简单的帮助类来编写并行化的循环。其核心思想是把代码写成生成器表达式的样子,然会再将它转换为并行计算: >>> fr

分享到:

栏目导航

推荐阅读

热门阅读