并行计算

Posted 【我是谁】

tags:

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

一、Parallel.Invoke执行多个方法

没有特定执行顺序,利用cpu多核并发执行,在运行并行方法前都会产生一些额外的开销,我们一定要测量运行结果相比没有并行是否有优化

  #region Parallel.Invoke
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Parallel.Invoke(Pinvoke1, Pinvoke2);
            watch.Stop();
            Console.WriteLine("Parallel run " + watch.ElapsedMilliseconds + " ms");
            watch.Reset();
            watch.Start();
            Pinvoke1();
            Pinvoke2();
            watch.Stop();
            Console.WriteLine("Normal run " + watch.ElapsedMilliseconds + " ms");
            Console.ReadKey();

            #endregion Parallel.Invoke
  private static void Pinvoke1()
        {
            Thread.Sleep(3000);
            Console.WriteLine("Pinvoke1 ThreadId="+Thread.CurrentContext.ContextID);
        }

        private static void Pinvoke2()
        {
            Thread.Sleep(2000);
            Console.WriteLine("Pinvoke2 ThreadId=" + Thread.CurrentContext.ContextID);
        }

 结果:

二、Parallel.For   每一次迭代一会当作一个任务交给一个线程,线程可能会重用,cpu决定

   Stopwatch watch = new Stopwatch();
            watch.Start();

            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine(i);
                Thread.Sleep(1000);
            }
            watch.Stop();
            Console.WriteLine(watch.Elapsed);
       
            watch.Restart();
            Parallel.For(0, 5, i =>
            {
                Console.WriteLine("===" + Thread.CurrentThread.ManagedThreadId + "===");
                Console.WriteLine(i);
                Thread.Sleep(1000);
            });
            watch.Stop();
            Console.WriteLine(watch.Elapsed);
            Console.ReadKey();

效果还是非常明显的,但是有个问题是Parallel.For是并行执行,每次迭代基本都会启用新线程,那么我们共用一个全局变量的时候,必须要加锁,出现资源等待,这时用并行就不合适了:示例如下:

object asyncObj = new object();
            long value = 0;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 9999; i++)
            {
                for (int j = 0; j < 19999; j++)
                {
                    value++;
                }
            }
            watch.Stop();
            Console.WriteLine("正常运行是" + watch.ElapsedMilliseconds + " ms");
            watch.Restart();
            Parallel.For(0, 9999, i =>
            {
                for (int j = 0; j < 19999; j++)
                {
                    lock (asyncObj)
                    {
                        value++;
                    }
                }
            });
            watch.Stop();
            Console.WriteLine("并行运行是" + watch.ElapsedMilliseconds + " ms");
            Console.ReadKey();

三、Parallel.ForEach  都foreach一样,只不过是并行的,也是每次循环一个线程处理

    List<Person> list = new List<Person>();
            for (int i = 1; i < 100; i++)
            {
                list.Add(new Person
                {
                    Age = i,
                    Name = "Name" + i
                });
            }
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Parallel.ForEach(list, p =>
            {
                Console.WriteLine("===" + Thread.CurrentThread.ManagedThreadId + "===");
                Console.WriteLine(p.Name);
            });
            watch.Stop();
            Console.ReadKey();

四、并行中断

ParallelLoopState:

1、Break-并行循环执行了当前迭代后尽快地停止执行。

//更改方法stop下面的示例,运行结果不一定哦,49,50都出现了

2、Stop-直接停止

//结果是50
  List<Person> list=new List<Person>();
            Parallel.For(0, 100, (num, state) =>
            {
                if (num<50)
                {
                    list.Add(new Person() {Age = num});
                }
                else
                {
                    state.Stop();
                }
            });
            Console.WriteLine(list.Count);

五、异常处理

并行执行的时候使用AggregateException获取全部异常,用Exception只能获取其中一个;

            try
            {
                Parallel.Invoke(Pinvoke1, Pinvoke2);
            }
            catch (AggregateException aggregateException)
            {
                foreach (var innerException in aggregateException.InnerExceptions)
                {
                    Console.WriteLine(innerException.Message);
                }
            }
    private static void Pinvoke1()
        {
            Thread.Sleep(3000);
            Console.WriteLine("Pinvoke1 ThreadId=" + Thread.CurrentContext.ContextID);
            throw new Exception("并行任务1抛出异常");
        }

        private static void Pinvoke2()
        {
            Thread.Sleep(2000);
            Console.WriteLine("Pinvoke2 ThreadId=" + Thread.CurrentContext.ContextID);
            throw new Exception("并行任务2抛出异常");
        }

 六、并行Linq (AsParallel,可以应用任何集合,提高处理速度)

  Stopwatch watch=new Stopwatch();
            List<Person> list = new List<Person>();
            Parallel.For(0,10000,p =>
            {
                list.Add(new Person() {Age = p,Name = "name"+p});//注意并行会出现有空对象的现象;也可以调用Invoke方法,里面写for循环就不会出现空对象
            });
            Console.WriteLine(list.Count);
            watch.Start();
            Console.WriteLine(list.Count);
            var persons = list.Where(c=>c!=null&&c.Age>100).ToList();
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
            watch.Restart();
            var parallelPersons = list.AsParallel().Where(c=> c != null && c.Age>100).ToList();
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
            Console.ReadKey();

  

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

如何实现C语言的多处理器并行计算

OpenGL 之 Compute Shader(通用计算并行加速)

goroutine简介

ViewPager的两个片段并行运行

线程池与并行度

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