C#并发编程之初识并行编程
寫在前面
之前微信公眾號里有一位叫sara的朋友建議我寫一下Parallel的相關(guān)內(nèi)容,因?yàn)槭种猩坛堑闹貥?gòu)工作量較大,一時之間無法抽出時間。近日,這套系統(tǒng)已有階段性成果,所以準(zhǔn)備寫一下Parallel的相關(guān)內(nèi)容,正好也延續(xù)之前的C#并發(fā)編程系列。
Parallel是并行編程的相關(guān)內(nèi)容,而Parallel.For和Parallel.Foreach又是并行編程中相當(dāng)重要的方法,所以不能孤立的去討論P(yáng)arallel,必須要放到并行編程的討論中去。
并行化,一般是對所要完成的任務(wù)進(jìn)行劃分,并且以并發(fā)的方式處理屬于自己的那份任務(wù),并且最終可以做到整合,所以并行化總會產(chǎn)生并發(fā)性。
實(shí)際上并行是并發(fā)的子集,并發(fā)和并行都可以多線程執(zhí)行,就看其處理器是否是多核的,這些線程能不能同時被cpu多個核執(zhí)行,如果可以就說明是并行,而并發(fā)是多個線程被cpu單核輪流切換著執(zhí)行。總之,只有在多核處理器上并行才會有意義。
并行化總會有著很大的挑戰(zhàn),即每一個部分以不同順序或者交錯執(zhí)行,都能保證最終結(jié)果的正確性,尤其涉及到各并行部分之間需要串行執(zhí)行的部分,這個挑戰(zhàn)是很大的。由于并行化程序設(shè)計要比普通的串行代碼復(fù)雜很多,也難維護(hù)很多,所以不是所有的問題都可以使用并行的。比如絕對執(zhí)行時間本來就很少,即使使用并發(fā)可以提高整體的執(zhí)行時間,那么我們也應(yīng)該使用傳統(tǒng)方式。但是如果主要涉及到提升用戶響應(yīng)能力的功能,那么我們推薦使用并行編程,同時處理分割后依然可以獨(dú)立進(jìn)行而不影響整體任務(wù)的功能也可以使用并行編程。
并行的相關(guān)實(shí)戰(zhàn)
說到并行,就需要先說下.NET FX4中引入的Task Parallel Library(任務(wù)并行庫),簡稱TPL。TPL主要覆蓋了三大使用場景,數(shù)據(jù)并行、任務(wù)并行和流水線,TPL以其高度的封裝特性,隱藏了并行編程里復(fù)雜的處理,使得開發(fā)人員可以以較低的門檻進(jìn)行并行編程。
數(shù)據(jù)并行
這種場景在于有大量數(shù)據(jù)需要處理,而且對每一份數(shù)據(jù)都要執(zhí)行的同樣的操作。
任務(wù)并行
有很多相對獨(dú)立的不同操作,或者可以分割成多個子任務(wù)但彼此之間是獨(dú)立的,就可以通過任務(wù)并行來發(fā)揮并行化的優(yōu)勢
流水線
流水線是以上兩種場景的結(jié)合,這個也是最復(fù)雜最難處理的場景,因?yàn)檫@里面涉及到多個并發(fā)的任務(wù)進(jìn)行協(xié)調(diào)處理。
此場景,奈何小編理解的不是很好,所以不敢亂寫,多方查找資料,找到了oschina上的一篇文章。
流水線技術(shù),指的是允許一個機(jī)器周期內(nèi)的計算機(jī)各處理步驟重疊進(jìn)行。特別是,當(dāng)執(zhí)行一條指令時,可以讀取下一條指令,也就意味著,在任何一個時刻可以有不止一條指令在“流水線”上,每條指令處在不同的執(zhí)行階段。這樣,即便讀取和執(zhí)行每條指令的時間保持不變,而計算機(jī)的總的吞吐量提高了。
原文地址:https://my.oschina.net/u/3374461/blog/1930305
System.Threading.Tasks.Parallel類
雖然Parallel類在System.Threading.Tasks命名空間下,但是創(chuàng)建并行代碼不一定要直接使用Task類的實(shí)例,我們可以直接使用Parallel靜態(tài)類所提供的方法。
Parallel.For:為固定數(shù)目的獨(dú)立For循環(huán)迭代提供了負(fù)載均衡式的并行執(zhí)行
Parallel.For(0, 5, i => {Console.WriteLine("the number is", i); });Parallel.Foreach:為固定數(shù)目的獨(dú)立ForEach循環(huán)迭代提供了負(fù)載均衡式的并行執(zhí)行。這個方法支持自定義分區(qū)器(Partitioner),以使得我們可以完全掌控數(shù)據(jù)分發(fā)。
string[] letters = new string[] {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"}; Parallel.ForEach(letters, i => Console.WriteLine("letter is " + i));Parallel.Invoke:為給定的獨(dú)立任務(wù)提供了負(fù)載均衡式的并行執(zhí)行,接下來會重點(diǎn)討論這個方法。
Parallel.Invoke
這個方法很實(shí)用,也很簡單。
以下代碼可以返回void的無參數(shù)方法:
Parallel.Invoke(Method1(), Method2(), Method3(), Method4()); 通過Lambda表達(dá)式運(yùn)行: Parallel.Invoke(() => Method1(), () => Method2(), () => Method3(), () => Method4());通過Lambda表達(dá)式和匿名類型來運(yùn)行:
Parallel.Invoke(() => {Method1();// Do something }, () => {Method2();// Do something}, () =>{Method3();// Do something}, () =>{Method4();// Do something });以上代碼需要并行執(zhí)行四個方法,但是如果空余邏輯內(nèi)核不足四個或者根本就沒有四個邏輯內(nèi)核,這四個方法是不能并發(fā)執(zhí)行的。因此在理想情況下,正好有至少四個空余邏輯內(nèi)核時,我們就可以并行執(zhí)行這四個方法了。
這四個方法,我們無法準(zhǔn)確的預(yù)測其執(zhí)行順序,因?yàn)檫@一切是由底層的邏輯會根據(jù)運(yùn)行時的現(xiàn)有可用資源創(chuàng)建出最合適的執(zhí)行計劃。當(dāng)然TPL依然有機(jī)制保證方法的順序執(zhí)行,這個以后我們再討論。
Parallel.Invoke最大的優(yōu)勢就是簡單,但是并不能因?yàn)樗唵?#xff0c;就不分場合的使用,事實(shí)上,我們需要在某些場景下權(quán)衡使用。
如果這四個方法的執(zhí)行時間不一致,那么就需要根據(jù)最長的執(zhí)行時間才能返回控制,這就可能造成一些邏輯內(nèi)核處于閑置狀態(tài)。所以我們需要預(yù)測一下大致的執(zhí)行時間,如果時間過長,那么就要認(rèn)真考慮是否真的需要使用這個方法。
其擴(kuò)展性很差,因?yàn)樗荒苷{(diào)用固定數(shù)目的邏輯內(nèi)核,剩余內(nèi)核就會一直處于閑置狀態(tài)。
方法之間的交互極其困難,極易產(chǎn)生Bug,當(dāng)然這是并行編程的常見問題,TPL也考慮到了這點(diǎn),也有足夠機(jī)制解決這個問題。
如果其中某個方法有了異常,捕捉異常會很困難,所以需要大家在相應(yīng)的被調(diào)用方法里編寫足夠的日志。
小編在以前的使用中還遇到了內(nèi)存溢出的異常,這些也會在以后的文章中說明其原因以及解決方法。
今天就寫到這兒吧,已經(jīng)十二點(diǎn)了,要休息一下,保護(hù)頭發(fā)了,哈哈哈。
總結(jié)
以上是生活随笔為你收集整理的C#并发编程之初识并行编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core分布式项目实战(
- 下一篇: C#黔驴技巧之去重(Distinct)