天天看點

5天玩轉C#并行和多線程程式設計 —— 第一天 認識Parallel

5天玩轉C#并行和多線程程式設計 —— 第一天 認識Parallel

随着多核時代的到來,并行開發越來越展示出它的強大威力!使用并行程式,充分的利用系統資源,提高程式的性能。

在.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. Stopwatch stopwatch = new Stopwatch();
  5. Console.WriteLine("Normal:");
  6. stopwatch.Start();
  7. RunOne();
  8. RunTwo();
  9. stopwatch.Stop();
  10. Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
  11. Console.WriteLine("----------------------------");
  12. Console.WriteLine("Parallel:");
  13. stopwatch.Restart();
  14. Parallel.Invoke(RunOne, RunTwo);
  15. Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
  16. #endregion
  17. Console.ReadKey();
  18. }
  19. static void RunOne()
  20. Thread.Sleep(2000);
  21. Console.WriteLine("The RunOne cost 2 seconds.");
  22. static void RunTwo()
  23. Thread.Sleep(3000);
  24. Console.WriteLine("The RunTwo cost 3 seconds.");

代碼很簡單,分别寫了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. 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. for (int i = 0; i < 10000; i++)
  3. for (int j = 0; j < 60000; j++)
  4. int sum = 0;
  5. sum += i;
  6. Parallel.For(0, 10000, i =>
  7. });

寫了兩個循環,做了一些沒有意義的事情,目的主要是為了消耗CPU時間,同理在main方法中調用,運作結果如下

  1. Normal cost 1682 milliseconds
  2. Parallel cost 575 milliseconds

可以看到,Parallel.For所用的時間比單純的for快了1秒多,可見提升的性能是非常可觀的。那麼,是不是Parallel.For在任何時候都比for要快呢?答案當然是“不是”,要不然微軟還留着for幹嘛?

修改一下代碼:

  1. object o=new object();
  2. long sum = 0;
  3. //int sum = 0;
  4. //sum += i;
  5. sum++;
  6. lock (o)

Parallel.For由于是并行運作的,是以會同時通路全局變量num,為了得到正确的結果,要加鎖,此時來看看運作結果:

  1. Normal cost 2549 milliseconds
  2. Parallel cost 21563 milliseconds

是不是大吃一驚啊?Parallel.For竟然用了21秒多,而for跟之前的差不多。這主要是由于并行同時通路全局變量,會出現任務的排程問題,大多數時間消耗在了任務的排程上面。

一直說并行,那麼從哪裡可以看出來Parallel.For是并行執行的呢?下面來寫個測試代碼:

  1. Parallel.For(0, 100, i =>
  2. Console.WriteLine(i);
  3. for (int i = 0; i < 100; i++)

從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. DoSomething(p);

二、 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. stopWatch.Start();
  4. Parallel.For(0, 1000, (i, state) =>
  5. if (bag.Count == 300)
  6. state.Break();
  7. //state.Stop();
  8. return;
  9. bag.Add(i);
  10. stopWatch.Stop();
  11. Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds);

2. 異常處理

首先任務是并行計算的,處理過程中可能會産生n多的異常,那麼如何來擷取到這些異常呢?普通的Exception并不能擷取到異常,然而為并行誕生的AggregateExcepation就可以擷取到一組異常。

  1. try
  2. Parallel.Invoke(RunOne, RunTwo);
  3. catch (AggregateException aex)
  4. foreach (var ex in aex.InnerExceptions)
  5. Console.WriteLine(ex.Message
Qt

繼續閱讀