自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

探究Visual Studio 2010中Parallel的使用

開發(fā) 后端
本文我們主要分析如何利用Parallel.For和Parallel.ForEach函數(shù)來并行化for循環(huán)和foreach循環(huán)。實(shí)際上,Parallel.For和Parallel.ForEach函數(shù)主要是針對(duì)“并行數(shù)據(jù)”的并行化操作。

之前51cto曾經(jīng)報(bào)道過關(guān)于Visual Studio 2010中Parallel類實(shí)現(xiàn)并行計(jì)算,本文我們主要分析如何利用Parallel.For和Parallel.ForEach函數(shù)來并行化for循環(huán)和foreach循環(huán)。實(shí)際上,Parallel.For和Parallel.ForEach函數(shù)主要是針對(duì)“并行數(shù)據(jù)”的并行化操作,所謂并行數(shù)據(jù),就是整個(gè)數(shù)據(jù)集中數(shù)據(jù)單元是相互獨(dú)立的,可以同時(shí)進(jìn)行處理。

在實(shí)際開發(fā)中,我們遇到的可以并行處理的不僅包括“并行數(shù)據(jù)”,還包括可以同時(shí)進(jìn)行的“并行邏輯”。所謂“并行邏輯”,就是相互獨(dú)立,可以同時(shí)執(zhí)行的多個(gè)任務(wù)。比如,程序員陳良喬每天早上要做兩件事情:燒水洗臉和鍛煉身體。這兩件事情就是相互獨(dú)立可以并行的,也就是說他在燒水的時(shí)候可以同時(shí)鍛煉身體。在以前的單核時(shí)代,CPU在同一時(shí)間只能完成一件事情,那么陳良喬只能先燒水后鍛煉,或者是先鍛煉后燒水,這導(dǎo)致他上班總是遲到。

進(jìn)入多核時(shí)代,CPU可以在同一時(shí)間完成多件事情了,借助.Net Framework 4.0中的Parallel類,我們可以方便地處理“并行邏輯”?,F(xiàn)在,程序員陳良喬可以一邊鍛煉一邊燒水,再也沒有遲到過了。他逢人便說:“Parallel真是個(gè)好東西!自從用了它,我腰也不酸了,背也不疼了,編程更有勁兒了”

使用Parallel.Invoke處理并行邏輯

跟Parallel.For函數(shù)相似,Parallel.Invoke也是Parallel類的一個(gè)靜態(tài)函數(shù),它可以接受一個(gè)Action[]類型的對(duì)象作為參數(shù),這個(gè)對(duì)象,就是我們要執(zhí)行的任務(wù)。系統(tǒng)會(huì)根據(jù)代碼運(yùn)行的硬件環(huán)境,主要是CPU運(yùn)算核心的個(gè)數(shù),自動(dòng)地進(jìn)行線程的創(chuàng)建和分配。這有些類似于我們所熟悉的多線程開發(fā),通過為每個(gè)線程指定一個(gè)線程函數(shù)而讓多個(gè)任務(wù)同時(shí)進(jìn)行,只是Parallel.Invoke函數(shù)簡(jiǎn)化了線程的創(chuàng)建和分配等繁瑣的動(dòng)作,我們只需要提供核心的線程函數(shù)就可以了。下面我們來看一個(gè)實(shí)際的例子。在上文中,我們介紹了程序員陳良喬起床的例子,在以前的單核時(shí)代,他起床大約是這個(gè)樣子的:

  1. // 串行式起床  
  2. private static void GetUp()  
  3. {  
  4.     Start("GetUp");  
  5.     // 先燒水  
  6.     boil();  
  7.     // 后鍛煉  
  8.     exercise();  
  9.     End("GetUp");  
  10. }  
  11.  
  12.  
  13. // 鍛煉  
  14. private static void exercise()  
  15. {  
  16.     Console.WriteLine("Exercise");  
  17.     Thread.Sleep(2000);  
  18.     Console.WriteLine("Finish Exercise");  
  19. }  
  20.  
  21. // 燒水  
  22. private static void boil()  
  23. {  
  24.     Console.WriteLine("Boil");  
  25.     Thread.Sleep(3000);  
  26.     Console.WriteLine("Finish Boil");  

 在單核時(shí)代,CPU在同一時(shí)間只能做一件事情,所以他只能先燒水,后鍛煉,這樣顯然會(huì)耽誤時(shí)間。一天,他又因?yàn)檫@事而遲到了,老板罵道,“你是豬啊,你不會(huì)用Parallel.Invoke一邊燒水一邊鍛煉啊?”于是,有了下面的并行式起床:

  1. // 并行式起床  
  2. private static void ParallelGetUp()  
  3. {  
  4.     Start("ParallelGetUp");  
  5.     // 在燒水的同時(shí),鍛煉身體  
  6.     var steps = new Action[] { () => boil(), () => exercise() };  
  7.     Parallel.Invoke(steps);  
  8.     End("ParallelGetUp");  

通過Parallel.Invoke函數(shù),我們將一些相互獨(dú)立的任務(wù)同時(shí)執(zhí)行,實(shí)現(xiàn)了“并行邏輯”,也大大地提高了應(yīng)用程序的性能和效率。從下面的截圖中,我們可以明顯地看出兩種方式的差別。串行方式所耗費(fèi)的時(shí)間,是兩個(gè)步驟的時(shí)間總和,而并行方式所耗費(fèi)的時(shí)間,大約是單個(gè)任務(wù)的耗時(shí)最長(zhǎng)的哪一個(gè)。

圖1 串行和并行的執(zhí)行情況 
圖1 串行和并行的執(zhí)行情況

#p#
對(duì)Parallel.Invoke進(jìn)行控制

Parallel.Invoke提供了一個(gè)重載版本,它可以接受一個(gè)ParallelOptions對(duì)象作為參數(shù),對(duì)Parallel.Invoke的執(zhí)行進(jìn)行控制。通過這個(gè)對(duì)象,我們可以控制并行的最大線程數(shù),各個(gè)任務(wù)是否取消執(zhí)行等等。例如,在一個(gè)智能化的家中,系統(tǒng)會(huì)判斷主人是否離開房間,如果主人離開了房間,則自動(dòng)關(guān)閉屋子里的各種電器。利用Parallel.Invoke我們可以實(shí)現(xiàn)如下:

  1. public static void PInvokeCancel()  
  2. {  
  3. // 創(chuàng)建取消對(duì)象  
  4. CancellationTokenSource cts = new CancellationTokenSource();  
  5. // 利用取消對(duì)象,創(chuàng)建ParallelOptions  
  6. ParallelOptions pOption = new ParallelOptions() { CancellationToken = cts.Token };  
  7. // 設(shè)置最大線程數(shù)  
  8. pOption.MaxDegreeOfParallelism = 2;  
  9.  
  10. // 創(chuàng)建一個(gè)守護(hù)監(jiān)視進(jìn)程  
  11. Task.Factory.StartNew(() => 
  12. {  
  13. Console.WriteLine("Cancellation in 5 sec.");  
  14. Thread.Sleep(5000);  
  15. // 取消,結(jié)束任務(wù)的執(zhí)行  
  16. cts.Cancel();  
  17. Console.WriteLine("Canceled requested");  
  18. });  
  19.  
  20. try  
  21. {  
  22. // 以ParallelOptions作為參數(shù),  
  23. // 調(diào)用Parallel.Invoke  
  24. Parallel.Invoke(pOption, () => ShutdownLights(pOption.CancellationToken),  
  25. () => ShutdownComputer(pOption.CancellationToken));  
  26.  
  27. //輸出執(zhí)行結(jié)果  
  28. Console.WriteLine("Lights and computer are tuned off.");  
  29. }  
  30. catch (Exception e)  
  31. {  
  32. Console.WriteLine(e.Message);  
  33. }  
  34. }  
  35.  
  36. private static void ShutdownLights(CancellationToken token)  
  37. {  
  38. while (!token.IsCancellationRequested)  
  39. {  
  40. Console.WriteLine("Light is on. " );  
  41. Thread.Sleep(1000);  
  42. }  
  43.  
  44. }  
  45. private static void ShutdownComputer(CancellationToken token)  
  46. {  
  47. while (!token.IsCancellationRequested)  
  48. {  
  49. Console.WriteLine("Computer is on." );  
  50. Thread.Sleep(1000);  
  51. }  

除了這種方式之外,ParallelOptions更多地應(yīng)用在取消任務(wù)隊(duì)列中還未來得及執(zhí)行的任務(wù)。當(dāng)我們限制了最大并發(fā)線程數(shù)的時(shí)候,如果需要通過Parallel.Invoke執(zhí)行的任務(wù)較多,則有可能部分任務(wù)在隊(duì)列中排隊(duì)而得不到及時(shí)的執(zhí)行,如果到了一定的條件這些任務(wù)還沒有執(zhí)行,我們可能取消這些任務(wù)。一個(gè)恰當(dāng)?shù)默F(xiàn)實(shí)生活中的例子就是火車站買票?;疖囌举I票的人很多,但是售票的窗口有限,當(dāng)?shù)搅讼掳鄷r(shí)間后,窗口就不再售票了,也就是剩下的售票任務(wù)需要取消掉。我們可以用下面的代碼來模擬這樣一個(gè)場(chǎng)景:

  1. public static void PInvokeCancel()  
  2.  
  3. {  
  4.  
  5. // 創(chuàng)建取消對(duì)象  
  6.  
  7. CancellationTokenSource cts = new CancellationTokenSource();  
  8.  
  9. // 利用取消對(duì)象,創(chuàng)建ParallelOptions  
  10.  
  11. ParallelOptions pOption = new ParallelOptions() { CancellationToken = cts.Token };  
  12.  
  13. // 設(shè)置最大線程數(shù),也就相當(dāng)于20個(gè)售票窗口  
  14.  
  15. pOption.MaxDegreeOfParallelism = 20;  
  16.  
  17. // 創(chuàng)建一個(gè)守護(hù)監(jiān)視進(jìn)程  
  18.  
  19. // 當(dāng)?shù)较掳鄷r(shí)間后就取消剩下的售票活動(dòng)  
  20.  
  21. Task.Factory.StartNew(() => 
  22.  
  23. {  
  24.  
  25. Console.WriteLine("Cancellation in 5 sec.");  
  26.  
  27. Thread.Sleep(5000);  
  28.  
  29. // 取消,結(jié)束任務(wù)的執(zhí)行  
  30.  
  31. cts.Cancel();  
  32.  
  33. Console.WriteLine("Canceled requested");  
  34.  
  35. });  
  36.  
  37. try  
  38.  
  39. {  
  40.  
  41. // 創(chuàng)建售票活動(dòng)  
  42.  
  43. Action[] CustomerServices = CreateCustomerService(1000);  
  44.  
  45. // 以ParallelOptions作為參數(shù),  
  46.  
  47. // 調(diào)用Parallel.Invoke  
  48.  
  49. Parallel.Invoke(pOption, CustomerServices);  
  50.  
  51. }  
  52.  
  53. catch (Exception e)  
  54.  
  55. {  
  56.  
  57. // 當(dāng)任務(wù)取消后,拋出一個(gè)異常  
  58.  
  59. Console.WriteLine(e.Message);  
  60.  
  61. }  
  62.  
  63. }  
  64.  
  65. // 創(chuàng)建售票的活動(dòng)  
  66.  
  67. static Action[] CreateCustomerService(int n)  
  68.  
  69. {  
  70.  
  71. Action[] result = new Action[n];  
  72.  
  73. for (int i = 0; i < n; i++)  
  74.  
  75. {  
  76.  
  77. result[i] = () => 
  78.  
  79. {  
  80.  
  81. Console.WriteLine("Customer Service {0}", Task.CurrentId);  
  82.  
  83. // 模擬售票需要的時(shí)間  
  84.  
  85. Thread.Sleep(2000);  
  86.  
  87. };  
  88.  
  89. }  
  90.  
  91. return result;  
  92.  

#p#
并行任務(wù)之間的同步

有時(shí)候我們?cè)谔幚聿⑿腥蝿?wù)的時(shí)候,各個(gè)任務(wù)之間需要同步,也就是同時(shí)執(zhí)行的并行任務(wù),需要在共同到達(dá)某一個(gè)狀態(tài)的后再一共繼續(xù)執(zhí)行。我們可以舉一個(gè)現(xiàn)實(shí)生活中的例子。陳良喬,賈瑋和單春暉是好朋友,他們相約到電影院看《建國(guó)大業(yè)》。他們?nèi)齻€(gè)住在不同的地方,為了能一起買票進(jìn)電影院,他們約好先在電影院門口的KFC會(huì)合,然后再一起進(jìn)電影院。這其中就涉及到一個(gè)同步的問題:他們需要先在KFC會(huì)合。他們是從家里分別到KFC的,但是需要在KFC進(jìn)行同步,等到三個(gè)人都到齊后在完成后后繼的動(dòng)作,進(jìn)電影院看電影。

為了完成并行任務(wù)之間的同步,.NET Framework中提供了一個(gè)類Barrier。顧名思義,Barrier就像一個(gè)關(guān)卡或者是剪票口一樣,通過Barrier類,我們可以管理并行任務(wù)的執(zhí)行,完成他們之間的同步。Barrier類的使用非常簡(jiǎn)單,我們只需要在主線程中聲明一個(gè)Barrier對(duì)象,同時(shí)指明需要同步的任務(wù)數(shù)。然后,在需要進(jìn)行同步的地方調(diào)用Barrier類的SignalAndWait函數(shù)就可以了。 當(dāng)一個(gè)并行任務(wù)到達(dá)SignalAndWait后,它會(huì)暫停執(zhí)行,等待所有并行任務(wù)都到達(dá)同步點(diǎn)之后再繼續(xù)往下執(zhí)行。下面我們以一個(gè)實(shí)際的例子,來看看如何利用Barrier類完成看電影的同步問題。

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading;  
  6. using System.Threading.Tasks;  
  7.  
  8. namespace ParallelBarrier  
  9.  
  10. {  
  11.  
  12. class Program  
  13.  
  14. {  
  15.  
  16. // 用于同步的Barrier對(duì)象  
  17.  
  18. static Barrier sync;  
  19.  
  20. static void Main(string[] args)  
  21.  
  22. {  
  23.  
  24. // 創(chuàng)建Barrier對(duì)象,這里我們需要同步  
  25.  
  26. // 任務(wù)有三個(gè)  
  27.  
  28. sync = new Barrier(3);  
  29.  
  30. // 開始執(zhí)行并行任務(wù)  
  31.  
  32. var steps = new Action[] { () => gotothecinema("陳良喬", TimeSpan.FromSeconds(5) ),  
  33.  
  34. () => gotothecinema("賈瑋", TimeSpan.FromSeconds(2) ),  
  35.  
  36. () => gotothecinema("單春暉", TimeSpan.FromSeconds(4) )};  
  37.  
  38. Parallel.Invoke(steps);  
  39.  
  40. Console.ReadKey();  
  41.  
  42. }  
  43.  
  44. // 任務(wù)  
  45.  
  46. static void gotothecinema(string strName, TimeSpan timeToKFC )  
  47.  
  48. {  
  49.  
  50. Console.WriteLine("[{0}] 從家里出發(fā)。", strName);  
  51.  
  52. // 從家里到KFC  
  53.  
  54. Thread.Sleep(timeToKFC);  
  55.  
  56. Console.WriteLine("[{0}] 到達(dá)KFC。", strName);  
  57.  
  58. // 等待其他人到達(dá)  
  59.  
  60. sync.SignalAndWait();  
  61.  
  62. // 同步后,進(jìn)行后繼動(dòng)作  
  63.  
  64. Console.WriteLine("[{0}] 買票進(jìn)電影院。", strName);  
  65.  
  66. }  
  67.  
  68. }  
  69.  

在這段代碼中,我們首先創(chuàng)建了Barrier對(duì)象,因?yàn)樵谶@里需要同步的任務(wù)有三個(gè),所以創(chuàng)建Barrier對(duì)象時(shí)是的參數(shù)是3。然后就是使用Parallel.Invoke執(zhí)行并行任務(wù)。我們?cè)诓⑿腥蝿?wù)gotothecinema中設(shè)置了一個(gè)同步點(diǎn),在這里我們調(diào)用Barrier對(duì)象的SignalAndWait函數(shù),它表示當(dāng)前任務(wù)已經(jīng)到達(dá)同步點(diǎn)并同時(shí)等待其他任務(wù)到達(dá)同步點(diǎn)。當(dāng)所有任務(wù)都到達(dá)同步點(diǎn)之后,再繼續(xù)往下執(zhí)行。運(yùn)行上面的程序,我們可以獲得這樣的輸出:

圖2 使用Barrier進(jìn)行同步 
圖2 使用Barrier進(jìn)行同步

#p#
更復(fù)雜的任務(wù)之間的同步

我們?cè)谑褂肂arrier進(jìn)行并行任務(wù)之間的同步時(shí),有這樣一個(gè)缺陷,我們需要預(yù)先知道所有需要同步的并行任務(wù)的數(shù)目,如果這個(gè)數(shù)目是隨機(jī)的,就無法使用Barrier進(jìn)行任務(wù)之間的同步了。并行任務(wù)數(shù)目不定這種情況很常見。我們還是來看上文中看電影的例子,每場(chǎng)進(jìn)電影院看電影的觀眾數(shù)目是不固定的,那么退場(chǎng)的觀眾也是不固定的,甚至還有中途退場(chǎng)的。當(dāng)所有觀眾都退場(chǎng)后,我們需要打掃電影院的衛(wèi)生。這里需要的同步的就是所有觀眾都退場(chǎng)。針對(duì)這種數(shù)目不定的多個(gè)并行任務(wù),.NET Framework提供了CountdownEvent這個(gè)類來進(jìn)行任務(wù)之間的同步。

就像它的名字一樣,CountdownEvent基于這樣一個(gè)簡(jiǎn)單的規(guī)則:當(dāng)有新的需要同步的任務(wù)產(chǎn)生時(shí),就調(diào)用AddCount增加它的計(jì)數(shù),當(dāng)有任務(wù)到達(dá)同步點(diǎn)是,就調(diào)用Signal函數(shù)減小它的計(jì)數(shù),當(dāng)CountdownEvent的計(jì)數(shù)為零時(shí),就表示所有需要同步的任務(wù)已經(jīng)完成,可以開始下一步任務(wù)了。下面我們利用CountdownEvent來模擬一下觀眾進(jìn)場(chǎng)立場(chǎng)的情景。

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading;  
  6. using System.Threading.Tasks;  
  7.    
  8.  namespace CountdownEventDemo  
  9.  
  10. {  
  11.    
  12.  // 觀眾類,用來表示一位觀眾  
  13.  
  14. class Customer  
  15.  
  16. {  
  17.  
  18. public Customer(int nID)  
  19.  
  20. {  
  21.  
  22. m_nID = nID;  
  23.    
  24. }  
  25.  
  26. // 觀眾的ID  
  27.  
  28. public int m_nID;  
  29.  
  30. }  
  31.  
  32. class Program  
  33.  
  34. {  
  35.  
  36. static void Main(string[] args)  
  37.  
  38. {  
  39.  
  40.  // 創(chuàng)建CountdownEvent同步對(duì)象  
  41.  
  42.  using (var countdown = new CountdownEvent(1))  
  43.  
  44. {  
  45.  
  46. // 產(chǎn)生一個(gè)隨機(jī)數(shù),表示觀眾的數(shù)目  
  47.  
  48. Random countRandom = new Random(DateTime.Now.Millisecond);  
  49.  
  50. int nCount = countRandom.Next(10);  
  51.  
  52. // 構(gòu)造每一位觀眾看電影的任務(wù)  
  53.  
  54. Action[] seeafilm = new Action[ nCount ];  
  55.  
  56. for (int i = 0; i < nCount; i++)  
  57.  
  58. {  
  59.  
  60. // 構(gòu)造Customer對(duì)象,表示觀眾  
  61.  
  62. Customer currentCustomer = new Customer( i+1 );  
  63.  
  64. seeafilm[i] = () => 
  65.  
  66. {  
  67.  
  68. // 觀眾進(jìn)場(chǎng)  
  69.  
  70. countdown.AddCount();  
  71.  
  72.  Console.WriteLine("觀眾 {0} 進(jìn)場(chǎng)。", currentCustomer.m_nID);  
  73.  
  74.  // 模擬看電影的時(shí)間  
  75.  
  76. Thread.Sleep(countRandom.Next(3000,6000));  
  77.  
  78. // 觀眾退場(chǎng)  
  79.  
  80. countdown.Signal();  
  81.  
  82. Console.WriteLine("觀眾 {0} 退場(chǎng)。", currentCustomer.m_nID);  
  83.  
  84. };  
  85.  
  86.  }  
  87.  
  88. //并行執(zhí)行任務(wù)  
  89.  
  90. Parallel.Invoke( seeafilm );  
  91.  
  92. // 在此同步,最后CountdownEvent的計(jì)數(shù)變?yōu)榱? 
  93.  
  94. countdown.Signal();  
  95.  
  96. countdown.Wait();  
  97.  
  98. }  
  99.  
  100. Console.WriteLine("所有觀眾退場(chǎng),開始打掃衛(wèi)生。");  
  101.  
  102. Console.ReadKey();  
  103.  

在這段代碼中,我們使用CountdownEvent進(jìn)行隨機(jī)個(gè)數(shù)任務(wù)之間的同步。最后,我們可以得到這樣的輸出。

圖3 使用CountdownEvent進(jìn)行同步 
圖3 使用CountdownEvent進(jìn)行同步

通過Parallel.Invoke函數(shù),我們可以輕松地將相互獨(dú)立的任務(wù)并行執(zhí)行,同時(shí)通過Barrier和CountdownEvent類進(jìn)行任務(wù)之間的同步。這種并行計(jì)算的開發(fā)方式,比以前那種基于線程的并行計(jì)算開發(fā)方式簡(jiǎn)便很多,解放了程序員的腦袋,讓他們可以把更多的腦力放到業(yè)務(wù)邏輯問題的解決之上。使用Parallel類,多快好省地開發(fā)并行計(jì)算應(yīng)用程序。
 

【編輯推薦】

  1. 教你如何用好Visual Studio 2010層次驗(yàn)證
  2. 關(guān)注Visual Studio 2010中代碼提示的改進(jìn)
  3. 細(xì)數(shù)VS 2003到Visual Studio 2010的開發(fā)之路
  4. Visual Studio 2010中Silverligh實(shí)現(xiàn)頁面動(dòng)態(tài)裝配
  5. Visual Studio 2010敏捷利劍:詳解Scrum
責(zé)任編輯:王曉東 來源: it168
相關(guān)推薦

2010-03-11 15:23:44

Visual Stud

2010-12-16 10:00:20

QtVisual Stud

2009-12-02 09:43:38

Visual Stud

2009-11-24 09:00:02

Visual Stud

2010-07-20 08:43:00

Visual Stud

2010-07-15 08:50:09

SharePointVisual Stud

2009-09-07 09:22:17

Visual Stud代碼片段

2010-02-26 09:18:24

Visual Stud

2010-03-16 14:32:16

Visual Stud

2009-09-02 16:21:17

Visual BasiC#語言

2009-11-10 13:43:37

Visual Stud

2009-03-17 08:56:57

Visual StudVS2010C++

2010-01-14 14:12:14

Visual Stud

2010-02-23 09:02:00

Visual Stud

2009-03-10 10:21:05

災(zāi)難恢復(fù)Restart Manvs

2010-04-15 08:40:00

UML建模Visual Stud

2009-12-16 14:46:06

Visual Stud

2011-01-07 09:01:17

Visual Stud

2009-11-11 09:48:06

Visual Stud

2010-02-22 16:43:33

Visual Stud
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)