一、Parallel的使用 在Parallel下麵有三個常用的方法Invoke、For和ForEach。 1、Parallel.Invoke 執行Parallel.ParallelInvokeMethod()的結果: 可以看出正常調用Run1()和Run2()所花費的時間在5秒左右,而用Parall ...
一、Parallel的使用
在Parallel下麵有三個常用的方法Invoke、For和ForEach。
1、Parallel.Invoke
1 public class ParallelDemo 2 { 3 private Stopwatch stopWatch = new Stopwatch(); 4 5 public void Run1() 6 { 7 Thread.Sleep(2000); 8 Console.WriteLine("Task 1 is cost 2 sec"); 9 } 10 public void Run2() 11 { 12 Thread.Sleep(3000); 13 Console.WriteLine("Task 2 is cost 3 sec"); 14 } 15 16 public void ParallelInvokeMethod() 17 { 18 stopWatch.Start(); 19 Parallel.Invoke(Run1, Run2); 20 stopWatch.Stop(); 21 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms."); 22 23 stopWatch.Restart(); 24 Run1(); 25 Run2(); 26 stopWatch.Stop(); 27 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms."); 28 } 29 }
執行Parallel.ParallelInvokeMethod()的結果:
可以看出正常調用Run1()和Run2()所花費的時間在5秒左右,而用Parallel.Invoke方法調用值用了3秒左右,也就是耗時最長的那個方法所花費的時間。
2、Parallel.For
這個方法和For迴圈類似。在ParallelDemo類中添加一個ParallelForMethod()方法:
1 public void ParallelForMethod() 2 { 3 stopWatch.Start(); 4 for (int i = 0; i < 10000; i++) 5 { 6 for (int j = 0; j < 60000; j++) 7 { 8 } 9 } 10 stopWatch.Stop(); 11 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms."); 12 13 stopWatch.Reset(); 14 stopWatch.Start(); 15 Parallel.For(0, 10000, item => 16 { 17 for (int j = 0; j < 60000; j++) 18 { 19 } 20 }); 21 stopWatch.Stop(); 22 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms."); 23 24 }
運行結果:
這個例子中Parallel.For比for耗費的時間要少,但是Parallel.For並不總是比for快。例如:
1 long sum = 0; 2 stopWatch.Start(); 3 for (int i = 0; i < 10000; i++) 4 { 5 for (int j = 0; j < 60000; j++) 6 { 7 sum++; 8 } 9 } 10 stopWatch.Stop(); 11 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms."); 12 13 stopWatch.Reset(); 14 stopWatch.Start(); 15 Parallel.For(0, 10000, item => 16 { 17 for (int j = 0; j < 60000; j++) 18 { 19 sum++; 20 } 21 }); 22 stopWatch.Stop(); 23 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");
用Parallel.For所耗費的時間反而更多,是for的3倍以上,這是因為Parallel.For是並行運行的,所以會同時訪問sum,會出現資源爭奪,耗費大量的時間在資源等待上面。
3、Parallel.ForEach
Parallel.ForEach與foreach類似
1 public void ParallelForEachMethod() 2 { 3 int[] nums = new int[100]; 4 for (int i = 0; i < 100; i++) 5 { 6 nums[i] = i; 7 } 8 stopWatch.Start(); 9 foreach (var item in nums) 10 { 11 Console.Write(item + "\t"); 12 } 13 stopWatch.Stop(); 14 Console.WriteLine("\nNormalFor run " + stopWatch.ElapsedMilliseconds + " ms."); 15 16 stopWatch.Reset(); 17 stopWatch.Start(); 18 Parallel.ForEach(nums, item => 19 { 20 Console.Write(item + "\t"); 21 }); 22 stopWatch.Stop(); 23 Console.WriteLine("\nParallelFor run " + stopWatch.ElapsedMilliseconds + " ms."); 24 }
從0到99輸出,從運行結果可以看出,Parallel.ForEach的輸出順序是不定的,而foreach是按順序輸出。
二、Parallel中途退出和異常處理
1、在串列操作中,使用break語句即可退出迴圈,但是再並行操作中,就不是這麼回事了。並行迴圈的委托參數中含有一個ParallelLoopState,該實例擁有Break()和Stop()這兩個方法,可以用來退出並行迴圈。
Break:當然這個是通知並行計算儘快的退出迴圈,比如並行計算正在迭代100,那麼break後程式還會迭代所有小於100的。
Stop:這個就不一樣了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。
下麵一段代碼測試:
1 public void ParallelBreak() 2 { 3 ConcurrentBag<int> bag = new ConcurrentBag<int>(); 4 Stopwatch stopWatch = new Stopwatch(); 5 stopWatch.Start(); 6 Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = 1 }, (i, state) => 7 { 8 if (bag.Count == 300) 9 { 10 state.Stop(); 11 return; 12 } 13 bag.Add(i); 14 }); 15 stopWatch.Stop(); 16 Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds); 17 }
當Parallel.For迴圈遍歷了300個元素時,就運行Stop()方法退出迴圈。
2、異常處理
1 public void Run1() 2 { 3 Thread.Sleep(2000); 4 Console.WriteLine("Task 1 is cost 2 sec"); 5 throw new Exception("Exception in task 1"); 6 } 7 public void Run2() 8 { 9 Thread.Sleep(3000); 10 Console.WriteLine("Task 2 is cost 3 sec"); 11 throw new Exception("Exception in task 2"); 12 } 13 public void Run3() 14 { 15 Thread.Sleep(5000); 16 Console.WriteLine("Task 3 is cost 5 sec"); 17 throw new Exception("Exception in task 3"); 18 } 19 20 public void ParallelInvokeMethod() 21 { 22 stopWatch.Start(); 23 try 24 { 25 Parallel.Invoke(Run1, Run2, Run3); 26 } 27 catch (AggregateException ex) 28 { 29 foreach (var item in ex.InnerExceptions) 30 { 31 Console.WriteLine(item.Message); 32 } 33 } 34 stopWatch.Stop(); 35 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms."); 36 37 stopWatch.Restart(); 38 try 39 { 40 Run1(); 41 Run2(); 42 Run3(); 43 } 44 catch (Exception ex) 45 { 46 Console.WriteLine(ex.Message); 47 } 48 stopWatch.Stop(); 49 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms."); 50 }
Parallel.Invoke中捕捉每個方法的異常,串列方法能捕捉到Run1的異常。