• Parallel World 3 – Parallel Loop (1)


    在这一系列文章中,我们将会经常用到下面的这些代码, 这些代码中有计算立方和与平方和的代码,没有什么具体意义,只是为了演示并行运算。

       1: class Program
       2:     {
       3:         private const int NUM_MAX = 100000000;
       4:  
       5:         private static void CubicSum()
       6:         {
       7:             double sum = 0;
       8:             var sw = Stopwatch.StartNew();
       9:             for (int i = 0; i < NUM_MAX; i++)
      10:             {
      11:                 sum = sum + Math.Pow((i * (i + 1) / 2), 2);
      12:             }
      13:             
      14:             Console.WriteLine("CubicSum Excuation Time: {0}",sw.Elapsed.ToString());
      15:             //Console.WriteLine("Cubic Result: {0}", sum);
      16:             //Debug.WriteLine(sw.Elapsed.ToString());
      17:         }
      18:  
      19:         private static void QuadraticSum()
      20:         {
      21:             double sum = 0;
      22:             var sw = Stopwatch.StartNew();
      23:             for (int i = 0; i < NUM_MAX; i++)
      24:             {
      25:                 sum = sum + (2 * i + 1) * (i + 1) * i / 6;
      26:             }
      27:  
      28:             Console.WriteLine("QuadraticSum Excuation Time: {0}", sw.Elapsed.ToString());
      29:             //Console.WriteLine("Quadratic Result: {0}", sum);
      30:             //Debug.WriteLine(sw.Elapsed.ToString());
      31:         }
      32:  
      33:         static void Main(string[] args)
      34:         {
      35:             var swSequence = Stopwatch.StartNew();
      36:             QuadraticSum();
      37:             CubicSum();
      38:             //Debug.WriteLine(swSequence.Elapsed.ToString());
      39:             Console.WriteLine("Sequence Excuation Time: {0}", swSequence.Elapsed.ToString());
      40:  
      41:             Console.WriteLine("--------------------------------------------------");
      42:  
      43:             var swParallel = Stopwatch.StartNew();
      44:             Parallel.Invoke(() => QuadraticSum(), () => CubicSum());
      45:             Console.WriteLine("Parallel Excuation Time: {0}", swParallel.Elapsed.ToString());
      46:             //Debug.WriteLine(swParallel.Elapsed.ToString());
      47:  
      48:             Console.WriteLine("Main method finish!");
      49:             Console.ReadLine();
      50:         }
      51:     }

    在System.Threading.Tasks.Parallel里提供了以下一些静态方法;我们可以通过下面的这些方法实现并行运算:

    Parallel

    I. Parallel.Invoke

    并行运行多个方法最简单的方法就是使用这个方法,注意这些方法最好是独立的。先看看并行执行的优势,执行上面的代码,你会看到如下的结果:

    Parallel

    第一次看到并行对于性能的提高是不是有些兴奋,不要着急在后续章节中,我们会深入到CPU的每个核中去看看,并且也会讲一下分析并行运算的性能问题。

    对于Parallel.Invoke方法有两个重载,第一个接受一个System.Action的数组

    Invoke1

    第二个增加了一个ParallelOptions (这个我们将在后续章节中讲解)

    Invoke2 

    可以用以下几种方式来使用Invoke

           1. 就像它的定义那样,定义一个Action数组,然后传递进去

       1: Action[] actionArray = new Action[3];
       2: actionArray[0] = new Action(() => Console.WriteLine("Action 0"));
       3: actionArray[1] = new Action(() => Console.WriteLine("Action 1"));
       4: actionArray[2] = new Action(() => Console.WriteLine("Action 2"));
       5:  
       6: Parallel.Invoke(actionArray);

    2. 直接把方法名做为参数,逐一传递进去

       1: static void Main(string[] args)
       2: {
       3:     Parallel.Invoke(Method1, Method2, Method3);
       4:     
       5:     Console.WriteLine("Main method finish!");
       6:     Console.ReadLine();
       7: }
       8:  
       9: static void Method1()
      10: {
      11:     Console.WriteLine("Method 1");
      12: }
      13:  
      14: static void Method2()
      15: {
      16:     Console.WriteLine("Method 2");
      17: }
      18:  
      19: static void Method3()
      20: {
      21:     Console.WriteLine("Method 3");
      22: }

    3. 当然你可以使用Lambda表达式、匿名函数和匿名委托

       1: static void Main(string[] args)
       2: {
       3:     Parallel.Invoke(
       4:         () => { Console.WriteLine("Method 1"); },
       5:         () => { Console.WriteLine("Method 2"); },
       6:         delegate() { Console.WriteLine("Method 3"); }
       7:         );
       8:     
       9:     Console.WriteLine("Main method finish!");
      10:     Console.ReadLine();
      11: }

    Invoke的缺点:

    1. 使用Invoke时,它所调用的方法是以一种不确定的先后顺序执行的,大家可以多次尝试上述代码,看看执行效果就知道了。因此对于一些比较复杂的并行算法,比如一些需要一个执行计划来控制并发方法的并行算法,简单的Invoke就不适用。

    2. 当它所调用的方法之间,运行时间差异很大的时候,它会运行所需的时间将会大于等于运行时间最长的那个方法所需的时间。因此导致CPU中的一些核长时间处于空闲状态。

    3. 因为它始终调用固定数量的方法,因此在一个多核的环境中,它可能仅仅使用其中的某几个核,而导致其他核处于空闲状态,比如把上述代码放到一个8核或者更多核的环境中,其中的几个核就会处于空闲状态。

    4. 如果它调用的方法之间,存在相互依赖,就会引起很难查找的并发问题,比如在开篇中提到的死锁;当然这个问题不仅仅是Invoke才会存在的问题。

    5. 如果在Invoke中抛出Exception,比通常的顺序执行抛出的异常要复杂一些, 所有调用的方法抛出的异常都会被打包放在一个System.AggregateException中,关于这一点我们会在后续文章中讲解到。

    Invoke的优点:

    1. 使用起来简单,不用担心那些Tasks或者Threads的问题。

  • 相关阅读:
    从新浪财经获取金融新闻类数据并进行打分计算
    SQL窗口函数的用法总结
    从新浪财经获取金融新闻类数据并保存到MySQL
    [ZJOI2015]幻想乡战略游戏
    二次剩余入门
    [多校赛20210406]迫害 DJ
    [NOI Online 2021 提高组] 愤怒的小N
    [NOI Online 2021 提高组] 岛屿探险
    「UNR #3」百鸽笼
    [ZJOI2019]开关
  • 原文地址:https://www.cnblogs.com/wildboar/p/2063857.html
Copyright © 2020-2023  润新知