寫在前面
并發程式設計一直都存在,隻不過過去的很長時間裡,比較難以實作,随着網際網路的發展,人口紅利的釋放,更加友好的支援并發程式設計已經成了主流程式設計語言的标配,而對于軟體開發人員來說,沒有玩過并發程式設計都會有點不好意思。本系列文章将會以C#語言為主,詳細介紹并發程式設計。
什麼是并發程式設計,其實很簡單,并發程式設計就是在一台處理器上同時做多件事情,并發程式設計的目标就是充分利用處理器的每一個核,以達到最高的處理性能。舉個例子,伺服器在響應第一個請求的同時響應第二個請求。
并發程式設計的方向
多線程
線程是一個獨立的運作單元,是作業系統中能夠進行運算排程的最小機關,它包含于程序之中,是程序中的實際運作機關。每個線程都有自己獨立的棧,但是與程序内的其他線程共享記憶體。現在的.NET程式都維護了一個線程池,裡面有着一定數量的工作線程,這些線程等待着執行配置設定下來的任務,線程池也可以随時監測線程的數量,以備開發者根據業務情況靈活處理。多線程也是我們并發程式設計的技術基礎。
并行程式設計
并行程式設計主要用于分解計算密集型的任務片段,并将其配置設定給多個線程。前提是,程式中的任務可以分割成多個互相獨立的任務塊,關鍵字是互相獨立,如果依賴太大,就不适合用并行程式設計。
并行程式設計利用CPU的空閑資源,充分提高了CPU的使用率,提高了系統的吞吐量。在大多數情況下,伺服器本身就已經具備了并行處理能力,當通過程式設計進行并行處理的時候,需要慎重,因為使用不當将會導緻記憶體溢出等風險,同時也會因為占用伺服器資源而導緻伺服器本身的并行處理能力顯著下降,嚴重的時候回導緻系統無法使用。是以在進行程式設計的時候,盡量不要處理過長或者過短的任務。
并行處理分為資料并行和任務并行,其實他們都使用到了動态調整的分割算法,在任務分割後配置設定給工作線程。可以通過以下兩種方式實作并行程式設計,一種是Parallel.ForEach以及更加優美的PLINQ,這是并行程式設計的推薦處理方式,并且它們自帶自動配置設定任務的算法,可以在運作時進行調整;
在編寫并行任務的時候,需要注意的是閉包所帶來的風險。因為閉包捕獲的是引用而不是值,是以可以在不經意間共享這些變量。一個比較好的處理就是,在使用閉包外的變量的時候,可以在閉包内定義局部變量,用以規避閉包帶來的變量共享問題。
需要說明的是,線程池會根據需要增加線程數量,線程池采用的是工作竊取隊列,以盡可能的達到高效
異步程式設計
目前最常用的異步程式設計模型是TAB程式設計(基于任務的程式設計模式)。異步程式設計提高了響應能力,也實作了可擴充性。比較直覺的是,大家在處理Winform的時候遇到過界面卡死的情況,異步程式設計可以在程式運作的過程中繼續相應使用者的輸入,而不會導緻界面卡死,并提高了提高伺服器端應用的TPS(Transactions Per Second)和 QPS (Queries Per Second)。
.NET4.5以後為異步程式設計引入了async和await關鍵字,async關鍵字加在方法聲明上,主要用來配合方法内的await關鍵字,這兩個關鍵字的引入,使得C#在異步程式設計上更加優雅。如下所示
1: public async Task DelayAsync()
2: {
3: await Task.Delay(1000);
4: }
異步程式設計的執行流程一般是,當系統運作至await,會暫停,并可以捕捉到目前的上線文,SynchronizationContext,如果該上線文為空,就會使用目前的TaskScheduler,該方法也會在這個上線文中繼續執行。代碼執行完以後,會嘗試在原始的上下文中恢複運作。
注意:運作winform和asp.net請求時會采用UI上下文或者asp.net上下文,其他情況下則采用線程池上下文。
異步方法的等待方式有await,Task.Wait和Task<T>.Result。但是要避免是用Task.Wait和Task<T>.Result,因為他們在UI線程或者ASP.NET線程環境中會導緻死鎖。這個地方需要說明一下死鎖問題
1: public async Task DelayAsync()
2: {
3: await Task.Delay(1000);//捕捉目前上下文,并試圖在已捕捉的上下文中繼續運作
4: }
5:
6: void Test()
7: {
8: Task task= DelayAsync();
9: Task.Wait();//同步程式塊,正在等待異步方法完成=======阻塞線程
10: }
UI或者asp.net的上下文每次隻能同時運作一個線程。Wait方法已經阻塞了一個線程,是以在await的時候無法捕捉上下文。可以使用ConfigureAwait方法,設定參數continueOnCapturedContext為false。由此,可以帶來一個啟示,就是線上程池線程上使用ConfigureAwait(false),在使用者界面或接口代碼中再恢複過來。
異步程式設計中有一條重要的準則就是,當你使用了異步程式設計的時候,最好一直使用,也是為了防止死鎖。
優化使用:
避免上下文延續,延續任務過多會導緻性能問題
如果一個async方法一個需要用到上下文一個不需要用到,可以考慮拆分為兩個async方法,這樣代碼組織也會更直覺。
寫到最後
以上隻是提出了C#并發程式設計的引子,後面将會詳細介紹C#并發程式設計的知識點。當然,C#并發程式設計還有其他内容,比如響應式程式設計和TPL資料流這些,我平時用的比較少,是以此處沒有再做介紹,有興趣的同學可以另外檢視一下。
以上為本篇文章的主要内容,希望大家多提意見,如果喜歡記得點個推薦哦
作者:
艾心
出處:
https://www.cnblogs.com/edison0621/
本文版權歸作者和部落格園共有,歡迎轉載,轉載時保留原作者和文章位址即可。