Go 協程為什么比進程和線程占用的系統資源低?
01 、介紹
進程是一個可執行程序在運行時的一塊獨立的虛擬內存[1]空間,Linux 給每個進程分配一個虛擬內存空間,包括棧空間、未使用內存、堆空間、BSS、DATA、TEXT 等。
線程可以理解為輕量級進程,多個線程“寄生”在一個進程中,每個線程有獨立的棧空間,其它虛擬內存空間,多個線程共享,所以線程之間通信比較簡單,也就是說線程之間可以通過共享內存通信。
進程和線程都是 CPU 的一個執行單元,在內核態切換,切換成本較高。
協程是用戶態的一個偽執行單元,在用戶態切換執行流程,切換成本較低。
02、切換執行單元的成本
我們通過介紹線程和協程的切換流程,講述為什么在內核態切換的成本較高,而在用戶態切換的成本較低?
因為進程和線程都是內核態切換,并且進程切換成本比線程切換成本更高,所以只介紹線程切換和協程切換的切換成本。
內核態切換 - 線程
在了解線程在內核態切換之前,我們先了解一下什么是 CPU 時間片[2],在操作系統中,我們會安裝很多軟件,并且我們會同時使用多個軟件,而 CPU 資源有限。
為了讓多個軟件可以在操作系統中同時運行,CPU 分成一個個的時間片,在每個時間片中運行一個軟件的一個線程,因為時間片非常短,所以我們會感覺多個軟件在同時運行。
在編寫代碼時,我們為了可以讓程序被分配到更多的 CPU 資源,可以多創建一些線程,用于提升程序運行的效率。需要注意的是,線程并不是創建越多越好。
因為 CPU 在內核態切換執行單元(線程)時,會有時間成本,在進行切換執行單元時,需要保存寄存器中的數據,將原執行單元的狀態保存,切換操作也會占用 CPU 資源(時間片),從而減少了供線程運行的 CPU 資源(時間片)。
除了時間成本之外,還會有性能開銷,系統內核調度線程,需要用戶空間和內核空間切換,因為只有擁有最高權限的內核空間才可以調度線程,限于篇幅,我們不再展開敘述。
用戶態切換 - 協程
因為通過創建線程(執行單元),為程序爭取更多的 CPU 資源,在線程切換時也會浪費 CPU 資源(時間成本),所以可以將執行單元不再在內核態運行,改為在用戶態運行,也就是協程。
協程的切換成本較低,是因為切換比較簡單,并且是在用戶態進行切換,切換的時間成本較低(納秒級),只需將當前協程的 CPU 寄存器的狀態先保存起來,然后將需要 CPU 資源的協程的 CPU 寄存器的狀態加載到 CPU 寄存器中。
關于 Go 協程的調度,我們在之前的文章中介紹過,此處不再贅述。
03 、內存占用
除了 CPU 資源有限之外,內存資源也是有限的,所以我們還需要了解進程、線程、協程的內存占用。
讀者朋友們應該知道 32 位操作系統只支持 4G 內存的內存條,這是因為進程在 32 位操作系統中最多只能占用 4G 內存,而在 64 位操作系統中可以占用更多內存。
線程占用內存一般是 10MB,不同的操作系統版本之間有些差異,區間在 4M - 64M。
協程占用內存最小,一個協程占用 2KB 左右的內存。
04 、總結
本文我們主要介紹為什么 Go 協程比進程和線程占用的系統資源低,通過進程、線程、協程的 CPU 資源和內存占用的比較,發現無論是在切換時消耗的 CPU 資源(時間片),還是內存占用,Go 協程都有明顯優勢。
一句話總結就是 Go 協程的切換成本和內存占用比線程和進程都低。
需要注意的是,Go 協程占用系統資源低,并不代表可以無限創建 Go 協程。