1. 程式人生 > >那些年我們一起追逐的多線程(Thread、ThreadPool、委托異步調用、Task/TaskFactory、Parallerl、async和await)

那些年我們一起追逐的多線程(Thread、ThreadPool、委托異步調用、Task/TaskFactory、Parallerl、async和await)

col 不同的 告訴 新的 OS 代碼區 monit strong list

一. 背景

  在剛接觸開發的頭幾年裏,說實話,根本不考慮多線程的這個問題,貌似那時候腦子裏也有沒有多線程的這個概念,所有的業務都是一個線程來處理,不考慮性能問題,當然也沒有考慮多線程操作一條記錄存在的並發問題,後面隨著處理的系統業務越來越復雜,多線程再也回避不了了,也就借此機會深入研究了一下.Net中的多線程的處理方案。

  發現在.Net領域領域中,多線程的處理大致經歷了這麽幾個階段:Thread→ThreadPool→委托的異步調用→Task→TaskFactory→Parallerl→異步編程模型(async和await)。

  關註我博客的人會發現,早在2017年6月份的時候,就開始整理多線程問題了,大約用了6篇文章的來介紹了.Net中的線程的使用方法,主要是介紹相應類的實例方法的使用,有點幫助文檔的意思了哦,最近多線程使用的相當頻繁,借此機會重新結合一些實際業務系統介紹一下.Net領域的多線程問題,本次將整合原先的六篇文章(刪除或覆蓋更新)。

PS: 多線程的本質是犧牲空間來換取時間,在同步方法中,邏輯代碼需要從上往下按順序執行代碼塊,在很多情況下代碼塊與代碼塊之間並沒有先後依賴關系,而前面的代碼塊非常耗時,在單線程下,後面的代碼塊必須等待前面的代碼塊執行完畢才能執行,在這種情況下,我們開辟出一個新線程去異步執行前面的耗時代碼塊,而主線程繼續往後執行,提高了執行效率,這就是犧牲了空間換取了時間(現在的cpu都是2核4線程、4核心8線程,完全有能力處理多個線程)。

下面補充一下多線程在時間和空間上的開銷:

(一). 時間上:

①:開啟或銷毀一個線程都會通知進出中的dll程序集,讓這些dll進行相應的操作。

②:時間片切換,cpu默認最大支持8線程,但你開啟了9個線程,必然有一個線程會休眠。

(二). 空間上:

①:用戶模式堆棧,一個線程分配1M的堆棧空間。

②:內核模式的堆棧,用戶模式的參數需要傳遞到內核模式。

③:線程的內核數據結構,會存放一下變量。

 

二. 概念的梳理

1. 進程、線程和多線程

  進程:當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源,而一個進程又是由多個線程組成。

  線程:線程是程序中的一個執行流,每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,即不同的線程可以執行同樣的函數。

  多線程:多線程是指程序中包含多個執行流,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,也就是說允許單個程序創建多個並行執行的線程來完成各自的任務。

2. 多線程的好處和弊端

  好處:可以提高CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU可以運行其它的線程而不是等待,這樣就大大提高了程序的效率。(犧牲空間資源,來換取時間)

  弊端:

  ①:線程也是程序,所以線程需要占用內存,線程越多占用內存也越多;(占內存多)

  ②:多線程需要協調和管理,所以需要CPU時間跟蹤線程; (占cpu多)

  ③:線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題;(多線程存在資源共享問題)

  ④:線程太多會導致控制太復雜,最終可能造成很多Bug。(管理麻煩,產生意外bug)

3. 何時建議使用多線程

  ①. 當主線程試圖執行冗長的操作,但系統會卡界面,體驗非常不好,這時候可以開辟一個新線程,來處理這項冗長的工作。

  ②. 當請求別的數據庫服務器、業務服務器等,可以開辟一個新線程,讓主線程繼續幹別的事。

  ③. 利用多線程拆分復雜運算,提高計算速度。

4. 何時不建議使用多線程

  當單線程能很好解決,就不要為了使用多線程而用多線程。

5. 同步調用和異步調用

  ①單線程同步調用:方法從上而下一次執行,一步一步執行,有先後順序。

技術分享圖片

  ②異步調用(區別於異步方法):開啟新的線程去執行業務,主線程單獨執行,可以選擇是否等待子線程執行完後再執行

    技術分享圖片

同步方法 VS 異步方法:

  1. 一個誤區:異步方法指的是一些特有的方法(並不開啟新線程),它和開啟一個新的線程比如“很多情況下我們會說,開啟一個新的線程去異步調用”,這不是一回事,典型的異步方法,比如js 的ajax請求。

  2. 同步方法:我們平時封裝的一些普通方法大多數都是同步方法,同步方法典型的特點:就是在沒有得到方法的返回值或者該方法沒有執行完,該調用就需要在這等待,不能繼續執行。

  3. 異步方法:異步方法在調用後,調用這在沒有得到返回結果前,就可以繼續執行後續業務,異步方法通常是通過通知、回調的方式告訴調用者,無須消耗過多的性能。

舉例1:

  $.Post("url",{},function(data){ });

  $("#div1").html("");

這兩行代碼,第一行發送異步請求的時候,即使得到回調返回值,下面清空div1內容的操作同樣也將執行,Post就是異步方法。

舉例2:

  先封裝1個方法: function Add(a,b){ 先休眠5s; return a+b}

  調用:

  Add(1,2);

  $("#div1").html("");

這兩行代碼,Add方法就屬於同步方法,所以必須等5s後,Add方法執行完,才能執行下面清空div1內容的操作。

  總結:同步方法和異步方法的區別就是:是否需要等待返回結果,才能執行後續操作。

  

6. 異步多線程的三個特點

  ①:同步方法卡界面,原因是主線程被占用;開啟新線程去異步調用不卡界面,原因是計算交給了別的線程,主線程空閑.

  ②:同步方法慢,原因是只有一個線程計算;開啟新線程去異步調用快,原因是多個線程同時計算,但是更消耗資源,不宜太多.

  ②:異步多線程是無序的,啟動順序不確定、執行時間不確定、結束時間不確定.

三. 系列章節

  第一節:復習委托,並且通過委托的異步調用開啟一個新線程和異步回調、異步等待。

  第二節:深入剖析Thread的五大方法、數據槽、內存柵欄。

  第三節:ThreadPool的線程開啟、線程等待、線程池的設置、定時功能。

  第四節:Task的啟動的四種方式以及Task、TaskFactory的線程等待和線程延續的解決方案。

  第五節:Task構造函數之TaskCreationOptions枚舉處理父子線程之間的關系。

  第六節:深入研究Task實例方法ContinueWith的參數TaskContinuationOptions。

  第七節:利用CancellationTokenSource實現任務取消和利用CancellationToken類檢測取消異常。

  第八節:Task的各類Task<TResult>返回值以及通用線程的異常處理方案。

  第九節:深究並行編程Parallel類中的三大方法 (For、ForEach、Invoke)和幾大編程模型(SPM、APM、EAP、TAP)

  第十節:利用async和await簡化異步編程模式的幾種寫法

  第十一節:深究用戶模式鎖的使用場景(異變結構、互鎖、旋轉鎖)

  第十二節:深究內核模式鎖的使用場景(自動事件鎖、手動事件鎖、信號量、互斥鎖、讀寫鎖、動態鎖)

  第十三節:實際開發中使用最多的監視鎖Monitor、lock語法糖的擴展、混合鎖的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)

  第十四節: 介紹四大並發集合類並結合單例模式下的隊列來說明線程安全和非安全的場景及補充性能調優問題。

  第十五節:

  第十六節:

  第十七節:

!

  • 作 者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,如需代碼請留下你的評論,加我QQ:604649488 (備註:評論的博客名)

那些年我們一起追逐的多線程(Thread、ThreadPool、委托異步調用、Task/TaskFactory、Parallerl、async和await)