c# 并行运算

Posted 咕咚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c# 并行运算相关的知识,希望对你有一定的参考价值。

c# 并行运算

1. Parallel.INVOKE() 看实例:

    private static Stopwatch watch = new Stopwatch();

        private static void Run1()
        {
            Thread.Sleep(2000);
            Console.WriteLine("Task 1 takes 2 sec");
        }
        private static void Run2()
        {
            Thread.Sleep(3000);
            Console.WriteLine("Task 2 takes 3 sec");
        }

        static void Main(string[] args)
        {
            watch.Start();
            Parallel.Invoke(Run1,Run2);
            watch.Stop();
            Console.WriteLine("Parallel run "+watch.ElapsedMilliseconds +" ms");
       


            watch.Restart();
            Run1();
            Run2();
            watch.Stop();
            Console.WriteLine("Normal run "+watch.ElapsedMilliseconds+" ms");

            Console.ReadLine();

看结果:

 

 

 2.Parallel.For

 看实例:

  watch.Start();
            for (int i = 0; i < 10000; i++)
            {
                for (int j = 0; j < 5000; j++)
                {
                    int sum = 0;
                    sum++;
                }
            }
            watch.Stop();
            Console.WriteLine("Normal run " + watch.ElapsedMilliseconds + "ms");

            watch.Restart();
            watch.Start();
            Parallel.For(0, 1000, item =>
              {
                  for (int j = 0; j < 5000; j++)
                  {
                      int sum = 0;
                      sum += item;
                  }

              });

            watch.Stop();
            Console.WriteLine("ParalleFor run " + watch.ElapsedMilliseconds + "ms");

            Console.ReadLine();

 

看结果:

 

是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?

看实例:

 

   var obj = new object();
            long num = 0;
            ConcurrentBag<long> bag = new ConcurrentBag<long>();
            watch.Start();
            for (int i = 0; i < 10000; i++)
            {
                for (int j = 0; j < 5000; j++)
                {
                    //    int sum = 0;
                    //    sum++;
                    num++;
                }
            }

            watch.Stop();
            Console.WriteLine("Normal run "+watch.ElapsedMilliseconds+ "Ms");

            watch.Restart();

            Parallel.For(0, 1000, item =>
            {
                for (int j = 0; j < 5000; j++)
                {
                    //int sum = 0;
                    //sum += item;
                    lock (obj)
                    {
                        num++; //全局变量,就要考虑到线程安全了
                        //这主要是由于并行同时访问全局变量,会出现资源争夺,大多数时间消耗在了资源等待上面。
                    }
                }

            });

            watch.Stop();
            Console.WriteLine("ParalleFor run " + watch.ElapsedMilliseconds + "ms");

            Console.ReadLine();

 

结果:(结果不是稳定的,你懂得~)

再看代码:

  Parallel.For(0, 100, i =>
              {
                  Console.Write( i +"\\t");
              });
           
            Console.ReadLine();

再看结果:

 

 傻孩子,这样你懂了吧~

 3.Parallel.Foreach

  

//Environment.ProcessorCount能够获取到当前的硬件线程数,所以这里也就开了2个区。
            Console.WriteLine(Environment.ProcessorCount);
            Console.ReadLine();
            //继续我的并发编程;

            //可以将数据进行分区,每一个小区内实现串行计算;分区采用Create实现;

            for (int j = 1; j < 4; j++)
            {
                Console.WriteLine("\\n第{0}次比较", j);
                ConcurrentBag<int> bag = new ConcurrentBag<int>();
                watch.Start();

                for (int i = 0; i < 300000; i++)
                {
                    bag.Add(i);
                }

                Console.WriteLine("串行计算:集合有{0},总共耗时:{1}",bag.Count,watch.ElapsedMilliseconds);

                GC.Collect();
                bag = new ConcurrentBag<int>();
                watch.Restart();
                Parallel.ForEach(Partitioner.Create(0, 300000), i =>
                 {
                     for (int m = i.Item1; m < i.Item2; m++)
                     {
                         bag.Add(m);
                     }
                 });
                Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);

                GC.Collect();
                watch.Stop();
            }           

            Console.ReadLine();

 

 结果:

 

 

 

4.parallel 中途退出循环

Break: 当然这个是通知并行计算尽快的退出循环,比如并行计算正在迭代100,那么break后程序还会迭代所有小于100的。

Stop:这个就不一样了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。

 

    ConcurrentBag<long> bag = new ConcurrentBag<long>();
            watch.Start();

            Parallel.For(0,1000, (i, state) =>
             {
                 if (bag.Count == 300)
                 {
                     state.Break();
                     //当数量达到300个时,会立刻停止;可以看到结果"Bag count is 300",如果用break,可能结果是300多个或者300个,大家可以测试一下。
                     return;
                 }
                 bag.Add(i);

             });
            watch.Stop();
            Console.WriteLine("Bag count is "+bag.Count+"  times is "+watch.ElapsedMilliseconds);

 

 异常处理

由于Task的Start方法是异步启动的,所以我们需要额外的技术来完成异常处理

 try  
    {  
        var parallelExceptions = new ConcurrentQueue<Exception>();  
        Parallel.For(0, 1, (i) => 
        {  
            try  
            {  
                throw new InvalidOperationException("并行任务中出现的异常");  
            }  
            catch (Exception e)  
            {  
                parallelExceptions.Enqueue(e);  
            }  
            if (parallelExceptions.Count > 0)  
                throw new AggregateException(parallelExceptions);  
        });  
    }  
    catch (AggregateException err)  
    {  
        foreach (Exception item in err.InnerExceptions)  
        {  
            Console.WriteLine("异常类型:{0}{1}来自:  
                {2}{3}异常内容:{4}", item.InnerException.GetType(),  
                Environment.NewLine, item.InnerException.Source,  
                Environment.NewLine, item.InnerException.Message);  
        }  
    }  
    Console.WriteLine("主线程马上结束");  
    Console.ReadKey();  

 

static void Main(string[] args)
        {
            try
            {
                Parallel.Invoke(Run1, Run2);
                //这个捕获
                //在不同的模式下,会有不同结果地呀;
                //debug 模式下,会停止的
                //realse 模式下就可以获取异常;
            }
            catch (AggregateException ex)
            {
                foreach (var single in ex.InnerExceptions)
                {
                    Console.WriteLine(single.Message);
                }
            }

            Console.Read();
        }

        static void Run1()
        {
            Thread.Sleep(3000);
            throw new Exception("我是任务1抛出的异常");
        }

        static void Run2()
        {
            Thread.Sleep(5000);

            throw new Exception("我是任务2抛出的异常");
        }

实例二

 try
            {
                go1();  //这样的异常只能捕获其中一个地呀;
                        
                go2();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            watch.Stop();
            Console.WriteLine("Normal run :" + watch.ElapsedMilliseconds);
            //尼玛的,这样的异常,居然捕获不到的地呀

 

 默认的情况下,底层机制会尽可能多的使用硬件线程,然而我们使用手动指定的好处是我们可以在2,4,8个硬件线程的情况下来进行测量加速比。 

class Program
    {
        static void Main(string[] args)
        {
            var bag = new ConcurrentBag<int>();

            ParallelOptions options = new ParallelOptions();

            //指定使用的硬件线程数为1
            options.MaxDegreeOfParallelism = 1;

            Parallel.For(0, 300000, options, i =>
            {
                bag.Add(i);
            });

            Console.WriteLine("并行计算:集合有:{0}", bag.Count);

        }
    }

 

以上是关于c# 并行运算的主要内容,如果未能解决你的问题,请参考以下文章

c# 并行运算

Sleep() 方法后的代码片段没有被执行

[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls(代码片段

C# 最有用的(自定义)代码片段是啥? [关闭]

c#代码片段快速构建代码

此 Canon SDK C++ 代码片段的等效 C# 代码是啥?