任务Task系列之Parallel的静态For,ForEach,Invoke方法

  本文参考书籍《CLR via C#》  

  一些常见的编程情形呢都可以通过任务来提升性能,为了简化编程,静态System.Threading.Tasks.Parallel封装了这些静态方法,其实它的内部仍然使用了Task对象,只是一些便捷编程的语法糖,但是十分好用。

  比如常见的循环:

  for(Int32 i=0;i<1000;i++) DoSometing();

  就可以用Parallel的for方法:

  Parallel.For(0, 10000, i => DoSometing(i));

  类似的有一个集合:

  foreach(var item in collection) DoSometing(item)

  就可用Palallel的ForEach:

  Palallel.ForEach(collection,item=>DoSometing(item))

  如果既可以用For,也可以用ForEach,那么建议使用For,因为它执行的更快,

  如果要执行多个方法:

  Method1();

  Method2();

  Method3();

  就可以并行执行:

  Palallel.Invole(

  ()=>Method1(),

  ()=>Method2(),

  ()=>Mathod3()

  );

  比如我们有一个求和的方法:

private static Int32 NumValue(Int32 n)
        {
            Int32 sum = 0;
            for (int i = 0; i < n; i++)
            {
                sum += i;
            }
            return sum;
        }

  如果使用Parallel.For()就可以写成

private static Int32 ParallelNumValue(Int32 n)
        {
            Int32 sum=0;
            Parallel.For(1, n, i => { sum += i; });
            return sum;
        }

  我们求累加到5000000,看一下串行求和和并行求和耗时的差别:

static void Main(string[] args)
        {
            Console.WriteLine("============耗时比较=============");
            {
                System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch();
                sw.Start();
                Int32 a = NumValue(5000000);
                sw.Stop();
                Console.WriteLine("串行求和得{0},耗时{1}ms",a, sw.ElapsedMilliseconds);
                sw.Restart();
                sw.Stop();
                Int32 b = NumValue(5000000);
                Console.WriteLine("并行求和得{0},耗时{1}ms",b,sw.ElapsedMilliseconds);
            }
            Console.WriteLine("=================================");
            Console.ReadLine();
        }

  测试结果如下:

  

  可见串行要更耗时些。

  Parallel的所有方法都让调用线程参与处理。从资源利用的角度说,这是一件好事,因为我们不望调用线程停下来(阻塞),等线程池线程做完所有工作オ能继续。然而,如果调用线程在线程池线程完成自己的那一部分工作之前完成工作,调用线程会将自己挂起,直到所有工作完成。这也是一件好事,因为这提供了和使用普通for或 foreach循环时相同的语义:线程要在所有工作完成后才继续运行。还要注意,如果任何操作抛出未处理的异常,你调用的Parallel方法最后会抛出一个AggregateException。但这不是说需要对自己的源代码进行全文替换,将for循环替换成对 Parallel.For的调用,将forech循环替换成对 Parallel. Foreach的调用。调用 Parallel的方法时有一个很重要的前提条件:工作项必须能并行执行!所以,如果工作必须顺序执行,就不要使用 Paralel的方法。另外,要避免会修改任何共享数据的工作项,否则多个线程同时处理可能会损坏数据。解決这个问题一般的办法是围绕数据访问添加线程同步锁。但这样一次就只能有一个线程访问数据,无法享受并行处理多个项所带来的好处。
  另外, Parallel的方法本身也有开销:委托对象必须分配,而针对每个工作项都要调用一次这些委托。如果有大量可由多个线程处理的工作项,那么也许能获得性能的提升。另外,如果每一项都涉及大量工作,那么通过委托来调用所产生的性能损失是可以忽略不计的。但如果只为区区几个工作项使用 Parallel的方法,或者为处理得非常快的工作项使用Parallel的方法,就会得不偿失,反而降低性能。
  

  

 

posted @ 2018-07-31 20:59  王小豆又叫小王子  阅读(547)  评论(0编辑  收藏  举报