• 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel


    5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

     
    原文: http://anneke.cn/ArticleInfo/Detial/23

    目录

    5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

    5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

    5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程 —— 第四天 Task进阶

    5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

    5天所有的Demo:Demo


    随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能。

    在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。这里面有很多关于并行开发的东西,今天第一篇就介绍下最基础,最简单的——认识和使用Parallel类。

    一、 Parallel类(提供对并行循环和区域的支持)的使用

    在Parallel类下有三个常用的方法Invoke,For,ForEach

    1. Parallel.Invoke:尽可能并行执行提供的每个操作(Executes each of the provided actions, possibly in parallel)

    微软官方对该方法的作用表达很明确了,就是尽可能的同时执行你提供的方法

    下面来看一个例子:新建一个控制台程序

    1. static void Main(string[] args)
    2. {
    3. #region Demo1
    4.  
    5. Stopwatch stopwatch = new Stopwatch();
    6. Console.WriteLine("Normal:");
    7. stopwatch.Start();
    8. RunOne();
    9. RunTwo();
    10. stopwatch.Stop();
    11. Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    12.  
    13. Console.WriteLine("----------------------------");
    14.  
    15. Console.WriteLine("Parallel:");
    16. stopwatch.Restart();
    17. Parallel.Invoke(RunOne, RunTwo);
    18. stopwatch.Stop();
    19. Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    20.  
    21. #endregion
    22. Console.ReadKey();
    23. }
    24. static void RunOne()
    25. {
    26. Thread.Sleep(2000);
    27. Console.WriteLine("The RunOne cost 2 seconds.");
    28. }
    29. static void RunTwo()
    30. {
    31. Thread.Sleep(3000);
    32. Console.WriteLine("The RunTwo cost 3 seconds.");
    33. }
    代码很简单,分别写了RunOne和RunTwo方法,等待一定的时间输出一句话,然后再main方法中用Stopwatch这个类来记录运行的总毫秒数,来比较串行和并行的运行效率

    结果如下:

    1. Normal:
    2. The RunOne cost 2 seconds.
    3. The RunTwo cost 3 seconds.
    4. Normal cost 5001 milliseconds
    5. ----------------------------
    6. Parallel:
    7. The RunOne cost 2 seconds.
    8. The RunTwo cost 3 seconds.
    9. Parallel cost 3010 milliseconds

    应该能够猜到,正常调用的话应该是5秒多,而Parallel.Invoke方法调用用了只有3秒,也就是耗时最长的那个方法,可以看出方法是并行执行的,执行效率提高了很多。

    2. Parallel.For:执行 for(在 Visual Basic 中为 For)循环,其中可能会并行运行迭代(Executes a for (For in Visual Basic) loop in which iterations may run in parallel.)

    这个方法和For循环功能相似,来写个例子看一下吧,还是控制台程序

    1. Stopwatch stopwatch=new Stopwatch();
    2. Console.WriteLine("Normal:");
    3. stopwatch.Start();
    4. for (int i = 0; i < 10000; i++)
    5. {
    6. for (int j = 0; j < 60000; j++)
    7. {
    8. int sum = 0;
    9. sum += i;
    10. }
    11. }
    12. stopwatch.Stop();
    13. Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    14. Console.WriteLine("----------------------------");
    15.  
    16. Console.WriteLine("Parallel:");
    17. stopwatch.Restart();
    18. Parallel.For(0, 10000, i =>
    19. {
    20. for (int j = 0; j < 60000; j++)
    21. {
    22. int sum = 0;
    23. sum += i;
    24. }
    25. });
    26. stopwatch.Stop();
    27. Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    写了两个循环,做了一些没有意义的事情,目的主要是为了消耗CPU时间,同理在main方法中调用,运行结果如下
    1. Normal:
    2. Normal cost 1682 milliseconds
    3. ----------------------------
    4. Parallel:
    5. Parallel cost 575 milliseconds

    可以看到,Parallel.For所用的时间比单纯的for快了1秒多,可见提升的性能是非常可观的。那么,是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?

    修改一下代码:

    1. object o=new object();
    2. long sum = 0;
    3. Stopwatch stopwatch = new Stopwatch();
    4. Console.WriteLine("Normal:");
    5. stopwatch.Start();
    6. for (int i = 0; i < 10000; i++)
    7. {
    8. for (int j = 0; j < 60000; j++)
    9. {
    10. //int sum = 0;
    11. //sum += i;
    12. sum++;
    13. }
    14. }
    15. stopwatch.Stop();
    16. Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    17. Console.WriteLine("----------------------------");
    18.  
    19. Console.WriteLine("Parallel:");
    20. stopwatch.Restart();
    21. Parallel.For(0, 10000, i =>
    22. {
    23. for (int j = 0; j < 60000; j++)
    24. {
    25. //int sum = 0;
    26. //sum += i;
    27. lock (o)
    28. {
    29. sum++;
    30. }
    31. }
    32. });
    33. stopwatch.Stop();
    34. Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
    Parallel.For由于是并行运行的,所以会同时访问全局变量num,为了得到正确的结果,要加锁,此时来看看运行结果:
    1. Normal:
    2. Normal cost 2549 milliseconds
    3. ----------------------------
    4. Parallel:
    5. Parallel cost 21563 milliseconds
    是不是大吃一惊啊?Parallel.For竟然用了21秒多,而for跟之前的差不多。这主要是由于并行同时访问全局变量,会出现任务的调度问题,大多数时间消耗在了任务的调度上面。

    一直说并行,那么从哪里可以看出来Parallel.For是并行执行的呢?下面来写个测试代码:

    1. Parallel.For(0, 100, i =>
    2. {
    3. Console.WriteLine(i);
    4. });
    5. for (int i = 0; i < 100; i++)
    6. {
    7. Console.WriteLine(i);
    8. }

    从0输出到99,运行后会发现输出的顺序不对,用for顺序肯定是对的,并行同时执行,所以会出现输出顺序不同的情况。

    3. Parallel.ForEach:执行 foreach(在 Visual Basic 中为 For Each)操作,其中在 IEnumerable 上可能会并行运行迭代(Executes a foreach (For Each in Visual Basic) operation on an IEnumerable in which iterations may run in parallel.)

    这个方法跟Foreach方法很相似,看看使用方法

    1. List<string> myList = new List<string>();
    2. Parallel.ForEach(myList, p =>
    3. {
    4. DoSomething(p);
    5. });

    二、 Parallel类中途退出循环和异常处理

    1. 当我们使用到Parallel类,必然是处理一些比较耗时的操作,当然也很耗CPU和内存,如果我们中途想停止,怎么办呢?

    在串行代码中我们break一下就搞定了,但是并行就不是这么简单了,不过没关系,在并行循环的委托参数中提供了一个ParallelLoopState类的实例,
    该实例提供了Break和Stop方法来帮我们实现。
    Break:告知 Parallel 循环应在系统方便的时候尽早停止执行当前迭代之外的迭代。
    Stop:告知 Parallel 循环应在系统方便的时候尽早停止执行。
    下面来写一段代码使用一下:
    1. ConcurrentBag<int> bag = new ConcurrentBag<int>();
    2. Stopwatch stopWatch = new Stopwatch();
    3.  
    4. stopWatch.Start();
    5. Parallel.For(0, 1000, (i, state) =>
    6. {
    7. if (bag.Count == 300)
    8. {
    9. state.Break();
    10. //state.Stop();
    11. return;
    12. }
    13. bag.Add(i);
    14. });
    15. stopWatch.Stop();
    16. Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds);

    2. 异常处理

    首先任务是并行计算的,处理过程中可能会产生n多的异常,那么如何来获取到这些异常呢?普通的Exception并不能获取到异常,然而为并行诞生的AggregateExcepation就可以获取到一组异常。

      1. try
      2. {
      3. Parallel.Invoke(RunOne, RunTwo);
      4. }
      5. catch (AggregateException aex)
      6. {
      7. foreach (var ex in aex.InnerExceptions)
      8. {
      9. Console.WriteLine(ex.Message
  • 相关阅读:
    python 实例方法、静态方法、类方法的区别
    locust 参数化实现
    Airtest 基于图像识别的自动化测试工具
    python 调用 dubbo 接口
    locust+geventhttpclient 性能优化
    python性能测试工具locust
    性能测试工具 wrk
    jmeter 参数化,关联参数,断言等使用说明
    Django上传excel表格并将数据写入数据库
    小程序 wx.uploadFile 上传文件 iOS 失败 400 错误排查
  • 原文地址:https://www.cnblogs.com/youmingkuang/p/6011078.html
Copyright © 2020-2023  润新知