CLR via C# —— 線程

來源:互聯網
上載者:User
線程的作用

        早期的作業系統沒有 "線程" 的概念, 例如16位的 Windows 就是一個不支援多線程的作業系統. 這樣的系統有一個特徵: 整個系統同時只運行著一個任務, 包含作業系統代碼還有應用程式的代碼. 這帶來了一個問題: 如果當前啟動並執行這個任務需要很長一段時間才能執行完成, 它就會阻止其它任務執行. 如果某個程式含有 bug, 程式進入了一個死迴圈, 使用者只好重新啟動電腦了.

        這樣的作業系統顯然很不給力, 微軟決定改進作業系統核心, 讓它的健壯性, 可靠性, 擴充性以及安全性都要得到提高. 微軟從1988年11月開始編寫 Windows NT, 微軟在設計這個系統核心的時候, 決定在一個進程中運行應用程式的每個執行個體, 進程則是應用程式的一個執行個體要使用的資源的集合. 每一個進程被賦予了一個虛擬位址空間, 確保一個進程無法訪問另一個進程的代碼和資料. 因為一個進程無法破壞另一個進程的代碼和資料, 所以系統的健壯性提高了; 程式無法訪問另一個應用程式的使用者名稱, 密碼等資訊, 因此安全性提高了.

        雖然資料無法被破壞, 而且更安全, 但如果一個應用程式進入無限迴圈, 機器只有1個 CPU 的話, CPU 就會執行無限迴圈, 系統仍然會停止回應. 微軟修正這個問題的辦法就是 "線程". 線程的職責是對 CPU 進行虛擬化, Windows 為每個進程都提供了該進程專用的線程(可以理解為系統給每一個進程都分配了一個 CPU, 只不過是虛擬), 當這個程式進入了一個無限迴圈, 並不會影響到其它程式的運行. 1993年7月推出的 Windows NT 3.1 是第一個支援多線程的 Windows 作業系統.

線程的開銷

        線程無疑是個好東西, 它讓作業系統即使在運行需要很長時間才能執行完的任務時也能隨時響應, 線程還允許使用者使用工作管理員強制終止似乎已經凍結的應用程式. 但好東西總是需要付出一定代價的!

線程的組成

        線程的每個部分都有一定的功能, 它們負責線程的建立, 進駐作業系統以及最後銷毀. 它們都需要時間和空間.

        1. 線程核心對象(thread kernel object)  每個線程都有這樣的一個資料結構, 它包含一組對線程進行描述的屬性, 還包含了線程上下文(thread context). 上下文是一個記憶體塊, 其中包含了 CPU 寄存器的集合. x86 的機器, 線程上下文大約有700位元組, x64IA64, 上下文大小分別約1240位元組和2500位元組.

        2. 線程環境塊(thread environment block, TEB)  TEB 是在使用者模式(應用程式代碼能快速存取的地址空間)中分配和初始化的一個記憶體塊. TEB 需要1個記憶體頁(x86 和 x64 CPU 中是4KB, IA64 CPU 中是8KB). TEB 包含線程的異常處理鏈首(head), 線程進入每個 try 塊都在鏈首插入一個節點, 退出 try 時, 會刪除改節點. 除此之外, TEB 還包含進程的 "執行緒區域儲存" 資料, 以及由 GDI 和 OpenGL 圖形使用的一些資料結構.

        3. 使用者模式棧(user-mode stack)  使用者模式棧用於儲存傳給方法的局部變數是實參. 它還包含一個地址, 指出當方法返回時, 線程接著應該從什麼地方開始執行. 預設情況下, Windows 為每個線程的使用者模式棧分配 1MB 的記憶體.

      4. 核心模式棧(kernel-mode stack)   應用程式的代碼經常需要叫用作業系統的核心模式的函數, 出於安全方面的考慮, OS會把調用的參數從 user mode stack 拷貝到 kernel mode stack, 拷貝完了後 OS 會對這些參數進行檢驗. 除此之外, 核心模式裡的方法也要互相調用, 它們就靠這個棧儲存局部變數, 方法參數和返回地址. 在32位系統上這個棧佔12KB,64位系統上佔24KB.

        5. DLL線程串連和線程分離通知(DLL thread-attach and thread-detach notifications)  當進程中建立了一個新線程, 都會調用該進程裡載入的所有 DLL 的 DllMain 方法, 並向方法傳遞一個 DLL_THREAD_ATTACH 標記. 當有一個線程終止時, 也會調用該進程裡載入的所有 DLL 的 DllMain 方法, 並向方法傳遞一個 DLL_THREAD_DETACH 標記. 不過對於C#以及其他託管語言編寫的 DLL, 因為沒有DllMain方法, 所以不會收到這通知, 這提升了效能. 對於非託管 DLL, 可以調用 Win32 DisableThreadLibraryCalls 來決定不理會這些通知.

線程的切換

        線程將 CPU 進行虛擬化後變成一個個的邏輯 CPU, 而物理 CPU 只有1個(多核CPU則有多個CPU), 一個 CPU 同時只能做一件事. 在任何時候, Windows 只將一個線程分配給一個 CPU, 那個線程允許運行一個"時間片", 一旦時間片到期, Windows 就環境切換到另一個線程,  每次環境切換都執行以下操作:

        1. 將 CPU 寄存器中的值儲存在當前正在啟動並執行線程核心對象的一個上下文結構中.

        2. 調度下一個線程運行. 如果這個線程屬於另一個的進程, Windows 還要先切換虛擬位址空間.

        3. 將所選線程的上下文結構中的值載入到 CPU 的寄存器中.

合理使用線程

        通常你使用線程可能為了將代碼和其它代碼隔離來提高程式的可靠性, 或者使用線程來簡化編碼或者使用線程來實現並發執行. 上面可以看出, 建立一個線程需要不少資源的, 建立一個線程就應該讓它做事, 而不是閑著.

        開啟工作管理員, 可以發現一些程式建立了很多線程, 但這個應用程式大部分佔用的 CPU 卻為0, 也就是說它不在做事情! 在編寫應用程式的時候, 可以考慮下建立這個線程是否有必要, 是否可以通過其它比較經濟方式來代替使用線程.

        實際上我不是很清楚為什麼 Jeffrey Richter 在第698頁說"Well, without a doubt, we can say
for sure that all of these applications we've just discussed are using threads
inefficiently.", 一個應用程式建立了很多線程, 就因為它們現在沒有在工作, 就認為它們是效率低下的?
如果那些線程是為了你在操作這個應用程式時獲得較高的響應, 我覺得這些暫時沒有被使用的線程是有存在的價值的. 如果你是從另外一個角度理解"效率低下",
覺得它大部分時間是不工作的, 你刪除它們, 如果你的應用程式響應變得不靈敏, 那又如何說呢?

線程調度和優先順序

        作業系統需要決定在什麼時間調度哪個線程, 並執行多長時間, 上下文結構反映了當前線程上一次執行時, 線程的 CPU 寄存器狀態, 在一個時間片之後, Windows 會檢查所有線程核心對象, 在這些對象中, 只有那些沒有正在等待什麼的線程才適合調度, 可以使用 Spy++ 查看每個線程被環境切換到的次數.

        優先順序用 0-31 表示, 0表示優先順序最低. 系統首先檢查優先順序為31的線程, 並使用輪流(round-robin)的方式調度他們. 只要存在可以調度的優先順序為31的線程, 系統永遠不會將優先順序為0-30的線程交給 CPU 來執行, 這種情況稱為饑餓(starvation). 實際上在編寫程式時, 我們不知道該為我剛剛建立的這個線程指定什麼優先順序, 為什麼是20而不是21? 所以 Windows 公開了優先順序系統的一個抽象層, 分別是 Lowest, Below Normal, Normal, Above Normal, Highest, 代碼中可以設定 Thread 的 Priority 屬性指定這個線程的優先順序別.

前台線程和後台線程

        這個概念不難理解. 比如有個應用程式建立了一個新的線程, 線程只可能是前台線程和後台線程這兩種, 如果建立的是前台線程, 只有當所有前台線程都執行完畢時程式才執行結束, 如果建立的是後台線程, 如果前台線程都結束了, 不管後台線程有沒有結束, 系統都將結束後台線程的運行, 且不會拋出什麼異常.

        可以通過設定線程執行個體的 IsBackground 屬性來決定這個線程是前台線程還是後台線程.

本文連結: http://www.cnblogs.com/technology/archive/2011/05/22/2053567.html

參考: Jeffrey Richter <CLR via C#>第三版第25章

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.