菜鳥學(xué)習(xí)并行編程,參考《C#并行編程高級(jí)教程.PDF》,如有錯(cuò)誤,歡迎指正。
C#并行編程-相關(guān)概念
C#并行編程-Parallel
C#并行編程-Task
C#并行編程-并發(fā)集合
C#并行編程-線程同步原語(yǔ)
C#并行編程-PLINQ:聲明式數(shù)據(jù)并行
背景
有時(shí)候必須訪問變量、實(shí)例、方法、屬性或者結(jié)構(gòu)體,而這些并沒有準(zhǔn)備好用于并發(fā)訪問,或者有時(shí)候需要執(zhí)行部分代碼,而這些代碼必須單獨(dú)運(yùn)行,這是不得不通過將任務(wù)分解的方式讓它們獨(dú)立運(yùn)行。
當(dāng)任務(wù)和線程要訪問共享的數(shù)據(jù)和資源的時(shí)候,您必須添加顯示的同步,或者使用原子操作或鎖。
之前的.NET Framework提供了昂貴的鎖機(jī)制以及遺留的多線程模型,新的數(shù)據(jù)結(jié)構(gòu)允許細(xì)粒度的并發(fā)和并行化,并且降低一定必要的開銷,這些數(shù)據(jù)結(jié)構(gòu)稱為輕量級(jí)同步原語(yǔ)。
這些數(shù)據(jù)結(jié)構(gòu)在關(guān)鍵場(chǎng)合下能夠提供更好的性能,因?yàn)樗鼈兡軌虮苊獍嘿F的鎖機(jī)制,如果在等待時(shí)間不短的情況下使用它們,這些原語(yǔ)會(huì)增加額外的開銷。
如果您需要特定的執(zhí)行順序,可以通過添加顯示同步來(lái)實(shí)現(xiàn)。
同步原語(yǔ)
.NET Framework 4在現(xiàn)在的System.Threading命名空間中提供了6個(gè)同步原語(yǔ),通過這個(gè)命名空間可以訪問遺留的線程類、類型和枚舉,還提供了新的基于任務(wù)的編程模型及特定情形緊密相關(guān)的數(shù)據(jù)結(jié)構(gòu)
Barrier 使多個(gè)任務(wù)能夠采用并行方式依據(jù)某種算法在多個(gè)階段中協(xié)同工作 通過屏障
CountdownEvent 表示在計(jì)數(shù)變?yōu)?時(shí)處于有信號(hào)狀態(tài)的同步基元 通過信號(hào)機(jī)制
ManualResetEventSlim允許很多任務(wù)等待直到另一個(gè)任務(wù)手工發(fā)出事件句柄,當(dāng)預(yù)計(jì)等待時(shí)間很短的時(shí)候,ManualResetEventSlim 的性能比對(duì)應(yīng)的重量級(jí)ManualResetEvent的性能要高。通過信號(hào)機(jī)制
SemaphoreSlim 限制對(duì)可同時(shí)訪問資源或資源池的線程數(shù),比對(duì)應(yīng)的Semaphore性能要高 通過信號(hào)機(jī)制
SpinLock 提供一個(gè)相互排斥鎖基元,在該基元中,嘗試獲得鎖的線程將在重復(fù)檢查的循環(huán)中等待,直至該鎖變?yōu)榭捎脼橹埂?/p>
SpinWait 提供對(duì)基于自旋的等待的支持。
通過屏障同步并發(fā)任務(wù) Barrier
當(dāng)在需要一組任務(wù)并行地運(yùn)行一連串的階段,但是每一個(gè)階段都要等待其他任務(wù)完成前一階段之后才能開始時(shí),您可以通過使用Barrier類的實(shí)例來(lái)同步這一類協(xié)同工作,通過屏障
下面貼代碼方便大家理解,如有問題,請(qǐng)指正,詳情見注釋:
class PRogram { private static Task[] _CookTasks { get; set; } private static Barrier _barrier { get; set; } /*獲取當(dāng)前計(jì)算機(jī)處理器數(shù)*/ private static int _particpants = Environment.ProcessorCount; /* coder:釋迦苦僧 * 代碼中 展示煮飯的步驟 1.打水 2.淘米 3.放入鍋中 4.蓋上鍋蓋 5.生火煮飯 */ static void Main(string[] args) { Console.WriteLine("定義{0}個(gè)人煮飯3次", _particpants); _CookTasks = new Task[_particpants]; _barrier = new Barrier(_particpants, (barrier) => { Console.WriteLine("當(dāng)前階段:{0}", barrier.CurrentPhaseNumber); }); Stopwatch swTask1 = new Stopwatch(); swTask1.Start(); /*定義N個(gè)人*/ for (int cook_person = 0; cook_person < _particpants; cook_person++) { _CookTasks[cook_person] = Task.Factory.StartNew((num) => { int index = Convert.ToInt32(num); /*每個(gè)人煮3次飯*/ for (int cook_count = 0; cook_count < 3; cook_count++) { CookStepTask1(index, cook_count); CookStepTask2(index, cook_count); CookStepTask3(index, cook_count); CookStepTask4(index, cook_count); CookStepTask5(index, cook_count); } }, cook_person); } /*ContinueWhenAll 提供一組任務(wù)完成后 延續(xù)方法*/ var finalTask = Task.Factory.ContinueWhenAll(_CookTasks, (tasks) => { /*等待任務(wù)完成*/ Task.WaitAll(_CookTasks); swTask1.Stop(); Console.WriteLine("采用并發(fā) {1}個(gè)人煮3次飯耗時(shí):{0}", swTask1.ElapsedMilliseconds, _particpants); /*釋放資源*/ _barrier.Dispose(); }); Thread.Sleep(4000); Stopwatch swTask = new Stopwatch(); swTask.Start(); /*定義N個(gè)人*/ for (int cook_person = 0; cook_person < _particpants; cook_person++) { /*每個(gè)人煮3次飯*/ for (int cook_count = 0; cook_count < 3; cook_count++) { CookStep1(cook_person, cook_count); CookStep2(cook_person, cook_count); CookStep3(cook_person, cook_count); CookStep4(cook_person, cook_count); CookStep5(cook_person, cook_count); } } swTask.Stop(); Console.WriteLine("不采用并發(fā) {1}個(gè)人煮3次飯耗時(shí):{0}", swTask.ElapsedMilliseconds, _particpants); Thread.Sleep(2000); Console.ReadLine(); } /*1.打水*/ private static void CookStepTask1(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 打水... 耗時(shí)2分鐘", pesron_index, index); Thread.Sleep(200); /*存在線程暫停 所以需要將 _barrier.SignalAndWait();放在方法中 */ _barrier.SignalAndWait(); } /*2.淘米*/ private static void CookStepTask2(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 淘米... 耗時(shí)3分鐘", pesron_index, index); Thread.Sleep(300); /*存在線程暫停 所以需要將 _barrier.SignalAndWait();放在方法中 */ _barrier.SignalAndWait(); } /*3.放入鍋中*/ private static void CookStepTask3(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 放入鍋中... 耗時(shí)1分鐘", pesron_index, index); Thread.Sleep(100); /*存在線程暫停 所以需要將 _barrier.SignalAndWait();放在方法中 */ _barrier.SignalAndWait(); } /*4.蓋上鍋蓋*/ private static void CookStepTask4(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 蓋上鍋蓋... 耗時(shí)1分鐘", pesron_index, index); Thread.Sleep(100); /*存在線程暫停 所以需要將 _barrier.SignalAndWait();放在方法中 */ _barrier.SignalAndWait(); } /*5.生火煮飯*/ private static void CookStepTask5(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 生火煮飯... 耗時(shí)30分鐘", pesron_index, index); Thread.Sleep(500); /*存在線程暫停 所以需要將 _barrier.SignalAndWait();放在方法中 */ _barrier.SignalAndWait(); } /*1.打水*/ private static void CookStep1(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 打水... 耗時(shí)2分鐘", pesron_index, index); Thread.Sleep(200); } /*2.淘米*/ private static void CookStep2(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 淘米... 耗時(shí)3分鐘", pesron_index, index); Thread.Sleep(300); } /*3.放入鍋中*/ private static void CookStep3(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 放入鍋中... 耗時(shí)1分鐘", pesron_index, index); Thread.Sleep(100); } /*4.蓋上鍋蓋*/ private static void CookStep4(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 蓋上鍋蓋... 耗時(shí)1分鐘", pesron_index, index); Thread.Sleep(100); } /*5.生火煮飯*/ private static void CookStep5(int pesron_index, int index) { Console.WriteLine("{0} 第{1}次 生火煮飯... 耗時(shí)30分鐘", pesron_index, index); Thread.Sleep(500); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }View Code
如代碼所示,在串行代碼中,雖然任務(wù)是有序進(jìn)行,但是等待的時(shí)間很長(zhǎng),因?yàn)橹皇窃谝粋€(gè)處理器下進(jìn)行處理,如下圖所示:
而采用并發(fā)處理中,使用Barrier,不僅保證了任務(wù)的有序進(jìn)行,還在性能損耗上得到了最大程度的降低,如下圖
ContinueWhenAll 提供一組任務(wù)完成后的延續(xù)方法
|
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注