.NET線程機制(一) 線程基礎


線程作用及開銷

  早期計算機一次只能運行一個程序,長時間執行程序容易出現計算機“癱瘓”的問題,如果程序進入死循環則只能重啟系統。即使計算機不崩潰,也難免讓用戶崩潰。為了解決這個問題,操作系統設計者設計出了進程的概念,使得每個應用程序運行在一個虛擬的內存空間中。進程中又包含多個線程,CPU則根據操作系統調度執行每個進程中的線程任務。通過線程這種對CPU的虛擬化管理方式,操作系統形成了多任務執行的機制。但與一切虛擬化機制一樣,線程會產生空間和時間的開銷。這其中的開銷包括:

1、線程內核對象。該數據結構中包含線程上下文。Windows在x86架構CPU上為每個線程內核對象分配的空間大約為700字節,x64和IA64架構CPU分別為大約1240字節和2500字節。

2、線程環境塊。占用一個內存頁,包含線程的異常處理鏈首。當線程進入try塊時,在鏈首插入一個節點,在線程對出退出try塊時,刪除該節點。另外線程環境塊中還包括一些其他的本地儲蓄數據。

3、用戶模式棧。用戶存儲傳給方法的局部變量和實參;還包含一個地址,指出當方法返回時,線程應該從什么地方開始接着執行。默認情況下每個線程的用戶模式棧分配1MB內存。

4、內核模式棧。記錄用戶程序調用內核模式函數時函數的實參。32位系統分配12KB內存,64位系統則分配24KB。

5、DLL線程連接和線程分離通知。進程中每創建和終止一個線程時,都會調用進程中加載的所有DLL的DllMain方法。

6、上下文切換。對單CPU計算機來說,操作系統每次只將一個線程分配給CPU執行,執行完后將線程上下文數據記錄下來保存在線程內核對象結構中;然后裝載另一個線程的上下文,將CPU執行控制交給此線程,如果該線程有另一個進程擁有,那么在裝載該線程之前,Windows還必須使得CPU能夠處理該虛擬地址空間。Windows操作系統為各個線程每次分配大概30毫秒的執行時間,稱為“時間片”。上下文切換是凈開銷,不會換來任何在存儲空間或者性能上的收益。但是能向用戶提供一個健壯的能靈活相應的操作系統。

空間開銷測試

測試代碼:

 1         static void Main(string[] args)
 2         {
 3             List<Thread> threadList = new List<Thread>();
 4             for (int i = 0; i < 1000; i++)
 5             {
 6                 Thread thread = new Thread(
 7                     new ParameterizedThreadStart(o => { Console.WriteLine("第{0}線程", o); Thread.Sleep(100000); })
 8                     );
 9                 threadList.Add(thread);
10             }
11             Console.ReadLine();
12             for (int i = 0; i < threadList.Count; i++)
13             {
14                 threadList[i].Start(i);
15             }
16             Console.ReadLine();
17         }

 

pslist的觀測結果結果:

程序開始運行時

所有線程Start以后

1000個線程Start以后虛擬物理內存占用量增加了31MB,而虛擬內存增加了1000MB之多。

線程池

  由於專用線程(實例化Thread類所創建的線程)存在的巨大內存和性能開銷,CRL被設計出支持線程池技術,為應用程序提供線程管理。每個CLR獨立維護一個自己的線程池,CLR在線程池中只建立必要的線程供給應用程序使用,在應用程序把多個任務分配給線程池后,CLR將任務輪流分配給線程池中線程來執行,當任務執行完畢后,線程池中的線程並不會回收,而是等待分配新的任務。這就能有效的減少線程的數量,並且減少了線程創建時的性能開銷(線程池相關的內容筆者正在整理之中,后續會陸續發布)。另外,對於Thread類有一個實例屬性IsBackground指示線程是前台還是后台運行,前台運行指當關閉線程所在進程時,線程不會等待執行完畢就關閉。后台線程是當進程結束運行時,線程即刻停止運行,不必等待線程運行完畢。(此處錯誤,感謝 聖靈石等朋友指正)前台線程指線程所在進程關閉時,進程需要等待線程執行完成才能關閉,后台進程指當進程關閉時,線程立即停止執行,不會等待執行完成既退出運行。該屬性默認值為True,即專用線程默認為前台線程。

進程、AppDomain和線程

  這里涉及到進程,AppDomain和線程,我覺得有必要把這三個概念放一起做一個大體的比對。

  1、進程是操作系統為應用程序虛擬的執行地址空間,應用程序中的所有數據都裝載在相互獨立的進程中運行。

  2、AppDomain是.NET托管應用裝載的內部相互隔離的托管執行空間。如:IIS進程中所有的Web應用都運行在獨立的AppDomain中。

  3、線程是應用程序內部虛擬化的CPU執行單元,操作系統對內存中所有應用程序進程中的線程進行調度,交給CPU進行執行。

  進程是一個虛擬的地址空間,操作系統不會對其進行調度而是調度執行其中包含的線程。CLR在AppDomain內部也有自己的運行線程,AppDomain中的線程由CLR維護,但最終CLR仍需將AppDomain中的線程映射為應用程序進程中的線程,交給操作系統進行調度。並且AppDomain中的線程不一定與操作系統線程完全一一對應。

  下圖是大體描述進程、AppDomain和線程的關系,以及操作系統對線程的調度。詳細內容本文不做進一步說明。

線程優先級

  線程調度根據線程優先級進行,Windows將系統內的線程分為0至31,共32個等級,優先級為31的線程是最高優先級線程,最先得到執行權限。例如,一個優先級為8的線程正在執行,而此刻操作系統確認一個優先級為31的線程已經做好了執行准備,那么操作系統會立刻掛起正在執行的線程,把CPU的執行權限交給優先級為31的線程,即使優先級為8的線程還沒有執行完一個完整的時間片,優先級為31的線程將獲得一個完整的時間片,如果該線程執行完后操作系統發現還有優先級為31的線程准備執行,那么CPU執行權限將分給這個線程,前面優先級為8的線程始終得不到執行,這種情況稱為飢餓。

  ​​如果開發人員沒有合理的設置自己程序內的線程優先級,就可能會造成其他應用程序很難得到執行,甚至影響計算機響應速度。所以Windows又設計了一個進程優先級類來控制各個進程中的線程優先級的關系,進程優先級是一個虛擬的概念,因為操作系統不會對進程進行調度,這個概念只是為了控制進程中線程優先級的范圍。進程優先級有6個級別(詳見下表),根據進程優先級類,應用程序對內部線程設置相對優先級,會得到一個操作系統調度的線程優先級值。這樣,使線程優先級能得到有效控制。

線程相對

優先級

進程優先級類

Idle

Below Normal

Normal

Above Normal

High

Real-Time

Time-critical

15

15

15

15

15

31

Highest

6

8

10

12

15

26

Above normal

5

7

9

11

14

25

Normal

4

6

8

10

13

24

Below normal

3

5

7

9

12

23

Lowest

2

4

6

8

11

22

Idle

1

1

1

1

1

16

完!

 

  聲明:本文內容屬個人理解,如有錯誤之處,請一定不吝指導。萬分感激。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM