天天看點

Go語言的線程模型-Goroutine機制

  大多數Go語言的使用者認為Go語言是網際網路時代的C語言,為應用而生,當然我也這麼覺得哈哈哈,聽君一席話真是如聽君一席話。

  目前可以實作并發執行的模型主要有四種,多程序、多線程、基于回調的非阻塞/異步IO、協程。

  • 多程序。多程序是在作業系統層面進行并發的基本模式。同時也是開銷最大的模式。在Linux平台上,很多工具鍊正是采用這種模式在工作。比如某個Web伺服器,它會有專門的程序負責網絡端口的監聽和連結管理,還會有專門的程序負責事務和運算。這種方法的好處在于簡單、程序間互不影響,壞處在于系統開銷大,因為所有的程序都是由核心管理的。
  • 多線程。多線程在大部分作業系統上都屬于系統層面的并發模式,也是我們使用最多的最有效的一種模式。目前,我們所見的幾乎所有工具鍊都會使用這種模式。它比多程序的開銷小很多,但是其開銷依舊比較大,且在高并發模式下,效率會有影響。
  • 基于回調的非阻塞/異步IO。這種架構的誕生實際上來源于多線程模式的危機。在很多高并發伺服器開發實踐中,使用多線程模式會很快耗盡伺服器的記憶體和CPU資源。而這種模式通過事件驅動的方式使用異步IO,使伺服器持續運轉,且盡可能地少用線程,降低開銷,它目前在Node.js中得到了很好的實踐。但是使用這種模式,程式設計比多線程要複雜,因為它把流程做了分割,對于問題本身的反應不夠自然。
  • 協程。協程(Coroutine)本質上是一種使用者态線程,不需要作業系統來進行搶占式排程,且在真正的實作中寄存于線程中,是以,系統開銷極小,可以有效提高線程的任務并發性,而避免多線程的缺點。使用協程的優點是程式設計簡單,結構清晰;缺點是需要語言的支援,如果不支援,則需要使用者在程式中自行實作排程器。目前,原生支援協程的語言還很少。
  • Go語言就是原生支援協程的語言,Go語言的線程用過的都知道是由go關鍵字來開啟,由channel來完成線程之間的通信,那麼再底層是由什麼架構來實作的呢?
  • Go語言的線程模型實際是協程的一種實作,叫goroutine機制。這個機制可以簡單分為四個部分,使用者線程,系統線程、使用者線程排程器和核心排程器。
  • 其中使用者線程由goroutine來維護,簡稱為G,包含了棧、指針等,系統線程可以簡單了解為CPU處理核心,簡稱為M-Machine,使用者現場排程器控制G和M的對應關系,簡稱為P-Processor,核心排程器排程系統線程和CPU核心的對應關系。為什麼說Go語言适用于高并發呢,答案就在這裡,因為G可以同時維護多個使用者線程,而G可以同時存在多個,P負責排程G與M的對應關系。簡單點說就是多個線程對應多個協程,多個協程對應多個系統線程,多個系統線程對應多個核心處理器,進而充分的利用計算資源。
  • 實際工作中,為了友善了解,這裡簡化了實際流程,P的作用遠不止這麼簡單,核心和系統線程的對應一般也是多對多的。假如我們在兩個雙核CPU(4個核心)上同時發起了16次運算請求-16個使用者線程,這16個x線程将進入4個G,也就是4個goroutine隊列,大概如下圖
Go語言的線程模型-Goroutine機制
  • 使用者發起的線程進入不同的Goroutine隊列,由P來排程G與M的對應,最後再CPU上完成運算。
  • 當然實際上的并發會有很多問題,比如線程阻塞、搶占式排程等問題,這些問題接下來再讨論。