深入理解計算機進程、線程和線程池

百劍閣 發佈 2022-09-19T09:08:10.095911+00:00

進程進程是作業系統構造的一個抽象概念,向應用程式隔離了CPU、內存資源分配的底層實現,可以簡單地將進程理解為運行中程序實例所使用資源的集合。

進程

進程是作業系統構造的一個抽象概念,向應用程式隔離了CPU、內存資源分配的底層實現,可以簡單地將進程理解為運行中程序實例所使用資源的集合。作業系統給每個進程賦予獨立的代碼執行流和私有的地址空間,讓一個進程中的代碼和數據無法被另外一個進程直接訪問(可以通過消息相互通信),從而提升系統的健壯性、安全性和可伸縮性。

私有地址空間

進程為每個程序提供了私有地址空間,就如同該程序獨占的使用系統地址空間。所謂地址空間,就是連續編號的字節,32 位系統地址空間大小是 2^32 字節(4096MB),這也是 32 位系統最大支持 4G 內存的原因,64 位系統理論上是 2 ^64 字節,這是非常大的數字,通常用不著如此之大的地址空間,具體的大小與作業系統有關。比如 Linux 256 TB, Winodows Win 8 以前是 16TB , 之後則是 256TB

私有地址空間分為多個區塊:包括代碼、數據、堆和棧。其中堆和棧都可在程序運行中動態擴容。私有地址空間或顯然是大於物理內存的大小的,因此兩者並不是一回事,我們可以將其稱之為虛擬內存。事實上,虛擬內存是作業系統內核為了對進程地址空間進行管理而引入的一個邏輯意義上的內存空間概念。

線程

進程很強大,但仍有缺憾。如果一個進程中的代碼出現死鎖,就會導致執行其代碼的 CPU 徹底失去功能,無法再執行任何其它計算。雖然現代計算機通常是多核的,一個 CPU 被死鎖不會造成整個系統的崩潰,但讓其中一個核心失去功能也是不可接受的。

由此,現代計算機又引入了線程的概念。線程進一步將 CPU 進行了虛擬化,一個進程可以容納多個線程,每個線程同樣貌似在單獨地使用 CPU,一個線程的代碼死鎖不會導致整個 CPU 的凍結。

CPU

現代計算機通常是超線程或者多核的。所謂超線程,就是讓一個 CPU 在邏輯上分為多個,作業系統將其視為多個獨立的 CPU ,但在其物理實現上其實是多組狀態資源(寄存器)共享一個執行單元,能夠提高CPU執行單元利用效率。多核則是將多個 CPU核封裝到一個晶片上,每個 CPU 核都有獨立的寄存器、告訴緩存和算術/邏輯運算單元,但多個CPU核可能會共享三級緩存。

另外,也有些計算機是多個 CPU 的,意思是在主板上直接有多個 CPU 接口,一次插入多個 CPU。但這在個人計算機上比較少見,因為對機箱空間、電源功率等的要求更高,成本也更貴。

多個 CPU(核)讓多線程編程變得更加有意義,因為代碼可以被多個CPU並行執行,因此可以有效提高性能。

抽象的代價

任何抽象都是有代價的,這個和人類社會一樣,任何管理活動也都是有成本的。進程和線程的代價就是,為了在不同進程或者線程間切換,作業系統就必須維護一個進程和線程的上下文,並執行相關的切換操作,也就是說,會帶來時間(CPU執行周期)和空間(內存)上的耗用。

以 64 位 Windows 線程為例,一個線程的線程內核對象占用約為 1KB、線程環境塊 4KB、內核模式棧 24KB、用戶模式棧 1MB。而現代計算機,系統運行的線程數基本都要數千個,筆者的電腦正常使用線程數通常在3000個左右,也就是說僅僅是為了維護線程就需要占用掉 3G 的內存。

從 時間角度來看,Windows 每隔 30 毫秒執行一次上下文切換,每次切換耗用的 CPU 周期會帶來性能損失,同時高速緩存中數據命中率的下降,也會在一定程度上帶來代碼執行性能的降低。因此,線程的創建、切換和銷毀是有成本的,而且還不低。

線程池

因為不同線程能在多個 CPU(核)上同時執行,因此多線程能夠有效提高計算效能。但對線程的管理本身是有成本的,因此並不是線程越多越好,不合理的使用線程甚至會降低效能。

通常的做法是使用線程池。有了線程池,線程在執行完任務後不會被銷毀,而是返回線程池等待重用,因此沒有額外的創建、切換和銷毀代價。當然,線程池的大小是有限的,如果你的應用使用的線程超過的線程池容量,還是會創建額外的線程的,但在絕大多數情況下,創建新線程的需要大幅減少。



關鍵字: