Go線程模型

介紹

先介紹一下常見的三種線程模型,然后再介紹Go中獨特的線程模型

三種線程模型

線程的并發(fā)執(zhí)行是由操作系統(tǒng)來調度的,然而操作系統(tǒng)一般在內核提供對線程的支持,我們在編程的過程中創(chuàng)建的線程是用戶線程,用戶線程與內核線程有什么關系呢?下面要講解的三種線程模型是根據用戶線程和內核線程關系的不用而區(qū)分的

一對一模型

這種線程模型是用戶線程與內核線程是一一對應的,當前進程對應的主線程就是用戶線程,也就是對應一個內核線程,如下圖所示:


一對一模型.png

這種線程模型的優(yōu)點在于在多處理器上,多個線程可以真正地實現并行,而不是只有并發(fā)。并且當一個線程由于執(zhí)行系統(tǒng)調用等原因被阻塞的時候,其他的線程不受影響。
缺點在于一般操作系統(tǒng)會限制內核線程的個數,所以用戶線程的個數也會受到限制。另外由于用戶線程與系統(tǒng)線程一一對應的關系,當用戶線程執(zhí)行系統(tǒng)調用的時候,需要從用戶態(tài)執(zhí)行的程序切換到內核態(tài),等執(zhí)行完系統(tǒng)調用后,又會從內核態(tài)切換回用戶態(tài),而這個切換的代價是非常大的。

多對一模型

多對一模型是指多個用戶線程對應一個內核線程,同一時刻同一個內核線程只能對應一個用戶線程。這時候對應同一個內核線程的多個用戶線程的上下文切換是在用戶態(tài)進行的,不由操作系統(tǒng)調用的。其模型圖如下:


多對一模型

這種模型的好處在于上下文切換是在用戶態(tài)進行的,所以切換的速度很快,開銷很小??蓜?chuàng)建的用戶線程也可以很多,只受內存的大小限制。這種模型由于是多個用戶線程對應一個內核線程,當該內核線程對應的一個用戶線程被阻塞掛起的時候,該內核線程的其他用戶線程也不能運行了。另外這種模型并不能很好地利用多核CPU進行并行運行。

多對多模型

多對多模型結合上述兩種模型的特點,讓大量的用戶線程對應少數幾個內核線程,其模型圖如下:


多對多模型

這時候每個內核線程對應多個用戶線程,每個用戶線程可以對應多個內核線程。當一個用戶線程阻塞后,其對應的內核線程會被阻塞,但是被阻塞的內核線程的其他用戶線程可以切換到其他內核線程上繼續(xù)運行,所以多對多模型是可以充分利用多核CPU提升運行效率的。多對多模型對用戶線程的個數沒有限制,理論上只要內存足夠就可以。

Go線程模型

Go線程模型屬于多對多線程模型,其模型圖如下:


Go線程模型

Go語言中使用go語句創(chuàng)建的goroutine可以認為是輕量級的用戶線程(協(xié)程也是輕量級的用戶線程),go線程模型包含三個概念:內核線程(M),goroutine(G),邏輯處理器(P),在Go中每個邏輯處理器(P)會綁定到某一個內核線程(M)上,每個邏輯處理器(P)內有一個本地隊列,用來存放goroutine。在上面介紹的多對多線程模型中是操作系統(tǒng)調度線程在物理CPU上運行,在Go中則是Go在運行時調度goroutine在邏輯處器(P)上運行的。
在Go中存在兩極調度,一級是操作系統(tǒng)的調度,該調度系統(tǒng)調度邏輯處理器占用CPU時間片運行的,還有一級是Go的運行時的調度系統(tǒng),該調度系統(tǒng)調度某個goroutine在邏輯處理器(P)上運行。
使用Go語句創(chuàng)建一個goroutine后,創(chuàng)建的goroutine會被放入Go運行時調度器的全局隊列中,然后Go運行時調度器會把全局隊列中的goroutine分配給不同的邏輯處理器(P),分配的goroutine會被放到邏輯處理器(P)的本地隊列中,當本地隊列中里的goroutine就緒并且待分配后,就會被分配到邏輯處理器(P)上運行。如上圖goroutine1當前正在占用邏輯處理器1在運行。
這里需要注意的是,為了避免某些goroutine出現饑餓現象,被分配到某一個邏輯處理器(P)上的多個goroutine是分時在該邏輯處理器(P)上運行的,而不是獨占邏輯處理器(P)運行到結束。比如某個goroutine從開始運行到結束運行需要5分鐘,那么當前邏輯處理器(P)下的另外其他goroutine并不是順序執(zhí)行的,而是交叉并發(fā)執(zhí)行的。
goroutine內部實現與在多個操作系統(tǒng)線程之間復用的協(xié)程(coroutines)一樣,如果有一個goroutine阻塞操作系統(tǒng)線程,則該操作系統(tǒng)線程對應的邏輯處理器(P)中其他的goroutine將遷移到其他操作系統(tǒng)線程中,以便它們可以繼續(xù)運行,如下圖所示:


切換邏輯處理器

如上圖左圖,假設goroutine1在執(zhí)行某些系統(tǒng)調用的時候,則goroutine1會導致內核線程1阻塞,這時候Go運行時調度器會把goroutine1所在的邏輯處理器1遷移到其他的內核線程上,這時候邏輯處理1上的goroutine2和goroutine3就不會受goroutine1的影響了。等goroutine1的系統(tǒng)調用執(zhí)行完后,goroutine1又會被Go運行時調度系統(tǒng)重新放入到邏輯處理器1的本地隊列。
默認情況下,Go是給每個可用的物理處理器都分配一個邏輯處理器(P),如果需要修改邏輯處理器(P)的個數可以通過函數runtime.GOMAXPROCS函數。

goroutine(G)的數量也是可以創(chuàng)建很多個,理論只要內存夠大,可以無限制創(chuàng)建。

總結

正是由于這樣的線程模型才讓Go中每臺機器可以創(chuàng)建成千上萬的goroutine。我們也需要特別了解其中的PMG模型。


2021.9.30 16:29 深圳

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容