操作系統的線程詳解
前言
這將是一個系列,一個關于進程、線程和 協程的系列。
主要用于:回顧和復習以往所學的知識 以及 希望這點經驗能夠幫助正在學習編程的你。
最初的幾章會講到一些相關的計算機理論知識,可能相對枯燥。但這是基礎,是理解后面程序必備的概念。如果有疑惑,可以加微信討論,咱們一起進步。
線程的定義
線程(thread)是操作系統能夠進行運算的最小單位。同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等等。
線程的實現方式
根據管理線程所發生的位置,我們將線程分為以下三類:
- 內核支持線程內核線程駐留在內核空間,它們是內核對象。有了內核線程,每個用戶線程被映射或綁定到一個內核線程。用戶線程在其生命期內都會綁定到該內核線程。一旦用戶線程終止,兩個線程都將離開系統。這被稱作”一對一”線程映射。優點是調度靈活,缺點是內核線程在用戶態和內核態之間進行上下文切換,資源開銷大。
- 用戶級線程內核對線程包一無所知。從內核角度考慮,就是按正常的方式管理,即單線程進程(存在運行時系統)。也就是說,如果因為進程中一個線程發生阻塞(如IO資源引起的阻塞),那么其他線程也將得不到執行。
- 組合方式線程(posix線程調度模型,又稱posix規范)posix線程調度是一個混合模型,很靈活,足以在標準的特定實現中支持用戶級和內核級的線程。模型中包括兩級調度–線程及和內核實體級。線程級與用戶級線程類似,內核實體由內核調度。由線程庫來決定它需要多少內核實體,以及他們是如何映射的。
上下文切換
- 什么是上下文切換?當下計算機系統中,CPU運行的線程數量遠超過CPU的核數,而用戶卻感覺這些程序在同時運行。這得益于分時系統的設計理念——人們將CPU的時間劃分為若干小段。每個線程只能在其中有限個小段上執行。待時間一到,CPU就會將線程掛起,使其進入就緒狀態,而CPU轉而執行下一個線程,只不過這個切換的過程對于用戶來說太快,感知不到而已。而在這一個過程中,CPU需要將當前線程的工作環境保存起來(遷出工作區),然后將即將執行的線程的數據載入工作區,這一過程我們稱之為上下文切換。拓展,上面說的是典型的被動上下文切換,又稱之為飛自愿性上下文切換。除此之外,還有因當前進程資源不足發生的自愿性上下文切換,以及,中斷信號導致的中斷上下文切換。
- 上下文切換的成本我在控制臺查看一下自己系統中上下文切換的次數。(滿載狀態下)
- $ sar -w 1 10
- Linux 4.15.0-153-generic (ubuntu-bionic) 08/09/21 _x86_64_ (2 CPU)
- 15:26:02 proc/s cswch/s
- 15:26:03 0.00 65892.00
- 15:26:04 0.00 248868.00
- 15:26:05 0.00 368564.00
- 15:26:06 0.00 391488.00
- 15:26:07 0.00 349058.00
- 15:26:08 0.00 353089.00
- 15:26:09 0.00 381352.00
- 15:26:10 0.00 379659.00
- 15:26:11 0.00 374822.00
- 15:26:12 0.00 129233.00
- Average: 0.00 304202.50
- # proc/s 每秒創建的任務總數。
- # cswch/s 每秒上下文切換的總數
我這是2核CPU,據統計目前主流的CPU每次上下文切換要花掉2~5微秒。我們取中位數3微秒。我們來計算一下每秒單核CPU花在上下文切換中的時間:3*304202/2/1000=0.456s。可以看出,頻繁的上下文切換對性能的影響是巨大的。
由于同一個進程中的多個線程是共享進程資源的,所以從這個角度我們又把上下文切換分成兩類:
- 進程切換:不同進程的線程之間切換。
- 線程切換:統一進程中不同線程之間的切換。

上圖綠色的部分代表需要切換的上下文,我們可以看出,線程切換的開銷遠小于進程切換。
今天線程的理論只是就講到這里,接下來我們將以Python為主要編程語言,帶大家進行 多線程實踐與性能分析,相對于干燥的理論知識,將會變得比較有趣。