5天玩轉C#並行和多線程編程 —— 第一天 認識Parallel


5天玩轉C#並行和多線程編程 —— 第一天 認識Parallel

C#
 
原文: 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM