進程、線程、協程與Goroutine

進程與線程(隔離與並行)

進程

進程可看作爲分配資源的基本單位。比如你new出了一塊內存,就是操作系統將一塊物理內存映射到你的進程地址空間上(進程創建必須分配一個完整的獨立地址空間),這塊內存就屬於這個進程,進程內的所有線程都可以訪問這塊內存,其他進程就訪問不了,其他類型的資源也是同理。所以進程是分配資源的基本單位,也是我們說的隔離。

線程

線程作爲獨立運行和獨立調度的基本單位,進而我們可以認爲線程是進程的一個執行流,獨立執行它自己的程序代碼。線程上下文一般只包含CPU上下文及其他的線程管理信息,線程創建的開銷主要取決於爲線程堆棧的建立而分配內存的開銷,這些開銷並不大。線程還分爲系統級別和用戶級線程,用戶級別線程對引起阻塞的系統調用的調用會立即阻塞該線程所屬的整個進程,而內核實現線程則會導致線程上下文切換的開銷跟進程一樣大,所以經常的折衷的方法是輕量級進程(Lightweight)。在 Linux 中,一個線程組基本上就是實現了多線程應用的一組輕量級進程。線程的作用就在於充分使用硬件CPU,也就是我們說的並行。

協程

協程的核心機制

在實模式編程下,理論上操作系統只能加載一個進程,那個時候進程要使用系統服務的方法很簡單,就是手工產生一箇中斷,就會觸發CPU的中斷處理機制,會保護好發起中斷的現場,會將當前執行地址設置爲對應的中斷處理函數的地址,處理完以後回到剛剛保存的現場。其實這個過程,本質上就是協程的核心流程了。

爲何使用中斷

基於中斷的方式,發起方和處理方可以使用自己的context,系統通過中斷的方法來達到提供系統服務的目的,一個很重要的原因就是可以保障在很多情況下,都能讓系統處理函數至少能有一個可用的context(屬於系統的資源),這樣當用戶進程的context資源耗盡的情況下,也能調用一些系統服務。

協程與線程

  1. 協程與線程主要區別是:協程將不再被內核調度,而是交給了程序自己;而線程是將自己交給內核調度。所以也不難理解golang中調度器的存在。
  2. 從我們應用角度來說,我們一般將協程理解爲用戶態輕量級線程,是對內核透明的,也就是系統並不知道有協程的存在,是完全由用戶的程序自己調度的,因爲是由用戶程序自己控制,那麼就很難像搶佔式調度那樣做到強制的CPU控制權切換到其他進程/線程,通常只能進行協作式調度,需要協程自己主動把控制權轉讓出去之後,其他協程才能被執行到。

協程與Goruntine

  1. 協程是Coroutine,Go語言的協程是Goruntine,兩者是不一樣的。
  2. Golang語言作者Rob Pike也說,“Goroutine是一個與其他goroutines 併發運行在同一地址空間的Go函數或方法。一個運行的程序由一個或更多個goroutine組成。它與線程、協程、進程等不同。它是一個goroutine“。
  3. Go 協程通過通道來通信,而協程通過讓出和恢復操作來通信。
  4. Go 協程比協程更強大。因爲Golang 在 runtime、系統調用等多方面對 goroutine 調度進行了封裝和處理,也就是Golang 有自己的調度器,工作方式基本上是協作式,而不是搶佔式,但也不是完全的協作式調度,例如在系統調用的函數入口處會有搶佔。當遇到長時間執行或者進行系統調用時,會主動把當前 goroutine 的CPU ( P) 轉讓出去,讓其他 goroutine 能被調度並執行,也就是我們爲什麼說 Golang 從語言層面支持了協程。簡單的說就是Golang自己實現了協程並叫做Goruntine
  5. Goruntine的優勢在於並行和非常低的資源使用,體現在內存消耗方面和切換(調度)開銷方面,每個 goroutine (協程) 默認佔用內存遠比 Java 、C 的線程少,只有2KB,而線程則需要8MB;線程切換涉及模式切換(從用戶態切換到內核態)、16個寄存器、PC、SP…等寄存器的刷新等;而goroutine 只有三個寄存器的值修改:PC / SP / DX。

GOMAXPROCS

官方解釋: GOMAXPROCS sets the maximum number of CPUs that can be executing simultaneously。 很清楚,就是限制CPU數,限制CPU數,本質上是什麼,就是限制並行數,並行數即同時執行數量,執行單元即線程,即限制最大並行線程數量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章