C#并發(fā)編程入門教程之概述
寫(xiě)在前面
并發(fā)編程一直都存在,只不過(guò)過(guò)去的很長(zhǎng)時(shí)間里,比較難以實(shí)現(xiàn),隨著互聯(lián)網(wǎng)的發(fā)展,人口紅利的釋放,更加友好的支持并發(fā)編程已經(jīng)成了主流編程語(yǔ)言的標(biāo)配,而對(duì)于軟件開(kāi)發(fā)人員來(lái)說(shuō),沒(méi)有玩過(guò)并發(fā)編程都會(huì)有點(diǎn)不好意思。本系列文章將會(huì)以C#語(yǔ)言為主,詳細(xì)介紹并發(fā)編程。
什么是并發(fā)編程,其實(shí)很簡(jiǎn)單,并發(fā)編程就是在一臺(tái)處理器上同時(shí)做多件事情,并發(fā)編程的目標(biāo)就是充分利用處理器的每一個(gè)核,以達(dá)到最高的處理性能。舉個(gè)例子,服務(wù)器在響應(yīng)第一個(gè)請(qǐng)求的同時(shí)響應(yīng)第二個(gè)請(qǐng)求。
關(guān)于并發(fā)編程的幾個(gè)誤解
誤解一:并發(fā)編程就是多線程
實(shí)際上多線只是并發(fā)編程的一中形式,在C#中還有很多更實(shí)用、更方便的并發(fā)編程技術(shù),包括異步編程、并行編程、TPL數(shù)據(jù)流、響應(yīng)式編程等。
誤解二:只有大型服務(wù)器程序才需要考慮并發(fā)
服務(wù)器端的大型程序要響應(yīng)大量客戶端的數(shù)據(jù)請(qǐng)求,當(dāng)然要充分考慮并發(fā)。但是桌面程序和手機(jī)、平板等移動(dòng)端應(yīng)用同樣需要考慮并發(fā)編程,因?yàn)樗鼈兪侵苯用嫦蜃罱K用戶的,而現(xiàn)在用戶對(duì)使用體驗(yàn)的要求越來(lái)越高。程序必須能隨時(shí)響應(yīng)用戶的操作,尤其是在后臺(tái)處理時(shí)(讀寫(xiě)數(shù)據(jù)、與服務(wù)器通信等),這正是并發(fā)編程的目的之一。
誤解三:并發(fā)編程很復(fù)雜、必須掌握很多底層技術(shù)
C# 和 .NET 提供了很多程序庫(kù),并發(fā)編程已經(jīng)變得簡(jiǎn)單多了。尤其是 .NET 4.5 推出了全新的 async 和 await 關(guān)鍵字,使并發(fā)編程的代碼減少到了最低限度。
并發(fā)編程的方向
多線程
線程是一個(gè)獨(dú)立的運(yùn)行單元,是操作系統(tǒng)中能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它包含于進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)行單位。每個(gè)線程都有自己獨(dú)立的棧,但是與進(jìn)程內(nèi)的其他線程共享內(nèi)存?,F(xiàn)在的.NET程序都維護(hù)了一個(gè)線程池,里面有著一定數(shù)量的工作線程,這些線程等待著執(zhí)行分配下來(lái)的任務(wù),線程池也可以隨時(shí)監(jiān)測(cè)線程的數(shù)量,以備開(kāi)發(fā)者根據(jù)業(yè)務(wù)情況靈活處理。
并行編程
并行編程主要用于分解計(jì)算密集型的任務(wù)片段,并將其分配給多個(gè)線程。前提是,程序中的任務(wù)可以分割成多個(gè)相互獨(dú)立的任務(wù)塊,關(guān)鍵字是相互獨(dú)立,如果依賴太大,就不適合用并行編程。
并行編程利用CPU的空閑資源,充分提高了CPU的利用率,提高了系統(tǒng)的吞吐量。在大多數(shù)情況下,服務(wù)器本身就已經(jīng)具備了并行處理能力,當(dāng)通過(guò)編程進(jìn)行并行處理的時(shí)候,需要慎重,因?yàn)槭褂貌划?dāng)將會(huì)導(dǎo)致內(nèi)存溢出等風(fēng)險(xiǎn),同時(shí)也會(huì)因?yàn)檎加梅?wù)器資源而導(dǎo)致服務(wù)器本身的并行處理能力顯著下降,嚴(yán)重的時(shí)候回導(dǎo)致系統(tǒng)無(wú)法使用。所以在進(jìn)行編程的時(shí)候,盡量不要處理過(guò)長(zhǎng)或者過(guò)短的任務(wù)。
并行處理分為數(shù)據(jù)并行和任務(wù)并行,其實(shí)他們都使用到了動(dòng)態(tài)調(diào)整的分割算法,在任務(wù)分割后分配給工作線程??梢酝ㄟ^(guò)以下兩種方式實(shí)現(xiàn)并行編程,一種是Parallel.ForEach以及更加優(yōu)美的PLINQ,這是并行編程的推薦處理方式,并且它們自帶自動(dòng)分配任務(wù)的算法,可以在運(yùn)行時(shí)進(jìn)行調(diào)整;
在編寫(xiě)并行任務(wù)的時(shí)候,需要注意的是閉包所帶來(lái)的風(fēng)險(xiǎn)。因?yàn)殚]包捕獲的是引用而不是值,所以可以在不經(jīng)意間共享這些變量。一個(gè)比較好的處理就是,在使用閉包外的變量的時(shí)候,可以在閉包內(nèi)定義局部變量,用以規(guī)避閉包帶來(lái)的變量共享問(wèn)題。
需要說(shuō)明的是,線程池會(huì)根據(jù)需要增加線程數(shù)量,線程池采用的是工作竊取隊(duì)列,以盡可能的達(dá)到高效
異步編程
目前最常用的異步編程模型是TAB編程(基于任務(wù)的編程模式)。異步編程提高了響應(yīng)能力,也實(shí)現(xiàn)了可擴(kuò)展性。比較直觀的是,大家在處理Winform的時(shí)候遇到過(guò)界面卡死的情況,異步編程可以在程序運(yùn)行的過(guò)程中繼續(xù)相應(yīng)用戶的輸入,而不會(huì)導(dǎo)致界面卡死,并提高了提高服務(wù)器端應(yīng)用的TPS(Transactions Per Second)和 QPS (Queries Per Second)。
.NET4.5以后為異步編程引入了async和await關(guān)鍵字,async關(guān)鍵字加在方法聲明上,主要用來(lái)配合方法內(nèi)的await關(guān)鍵字,這兩個(gè)關(guān)鍵字的引入,使得C#在異步編程上更加優(yōu)雅。如下所示
public async Task DelayAsync() { await Task.Delay(1000); }
異步編程的執(zhí)行流程一般是,當(dāng)系統(tǒng)運(yùn)行至await,會(huì)暫停,并可以捕捉到當(dāng)前的上線文,SynchronizationContext,如果該上線文為空,就會(huì)使用當(dāng)前的TaskScheduler,該方法也會(huì)在這個(gè)上線文中繼續(xù)執(zhí)行。代碼執(zhí)行完以后,會(huì)嘗試在原始的上下文中恢復(fù)運(yùn)行。
注意:運(yùn)行winform和asp.net請(qǐng)求時(shí)會(huì)采用UI上下文或者asp.net上下文,其他情況下則采用線程池上下文。
異步方法的等待方式有await,Task.Wait和Task<T>.Result。但是要避免是用Task.Wait和Task<T>.Result,因?yàn)樗麄冊(cè)赨I線程或者ASP.NET線程環(huán)境中會(huì)導(dǎo)致死鎖。這個(gè)地方需要說(shuō)明一下死鎖問(wèn)題
public async Task DelayAsync() { await Task.Delay(1000);//捕捉當(dāng)前上下文,并試圖在已捕捉的上下文中繼續(xù)運(yùn)行 } void Test() { Task task= DelayAsync(); Task.Wait();//同步程序塊,正在等待異步方法完成=======阻塞線程 }
UI或者asp.net的上下文每次只能同時(shí)運(yùn)行一個(gè)線程。Wait方法已經(jīng)阻塞了一個(gè)線程,所以在await的時(shí)候無(wú)法捕捉上下文??梢允褂肅onfigureAwait方法,設(shè)置參數(shù)continueOnCapturedContext為false。由此,可以帶來(lái)一個(gè)啟示,就是在線程池線程上使用ConfigureAwait(false),在用戶界面或接口代碼中再恢復(fù)過(guò)來(lái)。
異步編程中有一條重要的準(zhǔn)則就是,當(dāng)你使用了異步編程的時(shí)候,最好一直使用,也是為了防止死鎖。
優(yōu)化使用:
避免上線文延續(xù),延續(xù)任務(wù)過(guò)多會(huì)導(dǎo)致性能問(wèn)題
如果一個(gè)async方法一個(gè)需要用到上下文一個(gè)不需要用到,可以考慮拆分為兩個(gè)async方法,這樣代碼組織也會(huì)更直觀。
寫(xiě)到最后
以上只是提出了C#并發(fā)編程的引子,后面將會(huì)詳細(xì)介紹C#并發(fā)編程的知識(shí)點(diǎn)。當(dāng)然,C#并發(fā)編程還有其他內(nèi)容,比如響應(yīng)式編程和TPL數(shù)據(jù)流這些,我平時(shí)用的比較少,所以此處沒(méi)有再做介紹,有興趣的同學(xué)可以另外查看一下。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
C# 使用HttpClient上傳文件并附帶其他參數(shù)的步驟
這篇文章主要介紹了C# 使用HttpClient上傳文件并附帶其他參數(shù)的步驟,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2020-12-12Unity實(shí)現(xiàn)Flappy Bird游戲開(kāi)發(fā)實(shí)戰(zhàn)
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)Flappy Bird游戲開(kāi)發(fā)實(shí)戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12