Go語言中g(shù)oroutine的調(diào)度原理是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
江南網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),江南網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為江南上1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請找那個售后服務(wù)好的江南做網(wǎng)站的公司定做!在講goroutine的調(diào)度原理之前,有些與操作系統(tǒng)相關(guān)的知識,我們需要先知道,例如:
1.什么是并發(fā)?
并發(fā):兩個或兩個以上的任務(wù)在一段時間內(nèi)被執(zhí)行。我們并不關(guān)心這些任務(wù)是否在同一時刻執(zhí)行,我們只是知道,這些任務(wù)在這一段時間能能夠都被執(zhí)行,當(dāng)然這一段時間可以很長,也可以很短。
2.并發(fā)的最小并發(fā)單位是什么?
進(jìn)程是計算機(jī)資源分配最小的單位,是CPU分配資源的基本單位,具有獨(dú)立的內(nèi)存。
線程是計算機(jī)調(diào)度最小的單位,也是程序執(zhí)行的最小單位,是在進(jìn)程中的,一個進(jìn)程往往會有一個到多個線程。
3.計算機(jī)是如何實(shí)現(xiàn)并發(fā)的?
計算機(jī)的分時調(diào)用是并發(fā)的根本,CPU通過快速的切換作業(yè)來執(zhí)行不同的作業(yè),基本的調(diào)度單位在執(zhí)行的時候可以被阻塞掉,此時就會將CPU資源讓出來,等到該調(diào)度單位再次被喚醒的時候,又可以使用CPU資源,而操作系統(tǒng)保證了整個的調(diào)度過程。
除此之外,關(guān)于goroutine的調(diào)度原理,我們需要弄清楚下面幾個問題。
1.goroutine是什么?
Goroutine:是Go里的一種輕量級線程——協(xié)程。
1)相對線程,協(xié)程的優(yōu)勢就在于它非常輕量級,進(jìn)行上下文切換的代價非常的小。
2)對于一個goroutine ,每個結(jié)構(gòu)體G中有一個sched的屬性就是用來保存它上下文的。這樣,goroutine 就可以很輕易的來回切換。
3)由于其上下文切換在用戶態(tài)下發(fā)生,根本不必進(jìn)入內(nèi)核態(tài),所以速度很快。而且只有當(dāng)前goroutine 的 PC, SP等少量信息需要保存。
4)在Go語言中,每一個并發(fā)的執(zhí)行單元為一個goroutine。
Go 語言中的goroutine并發(fā), 采用的是CSP(communicating sequential processes)并發(fā)模型,講究的是以通訊的方式來進(jìn)行數(shù)據(jù)共享,是通過goroutine配合channel的方式來實(shí)現(xiàn)的。(備注:這部分知識后續(xù)單獨(dú)整理一章。)
2.既然它是比線程還小的粒度,那么它與線程有什么關(guān)系?
Go語言的線程模型就是一種特殊的兩級線程模型,如下所示:
兩級線程模型的實(shí)現(xiàn)非常復(fù)雜,和內(nèi)核級線程模型類似,一個進(jìn)程中可以對應(yīng)多個內(nèi)核級線程,但是進(jìn)程中的線程不和內(nèi)核線程一一對應(yīng);這種線程模型會先創(chuàng)建多個內(nèi)核級線程,然后用自身的用戶級線程去對應(yīng)創(chuàng)建的多個內(nèi)核級線程,自身的用戶級線程需要本身程序去調(diào)度,內(nèi)核級的線程交給操作系統(tǒng)內(nèi)核去調(diào)度。
我們先來看下,Go線程實(shí)現(xiàn)了MPG模型:
S(Sched):結(jié)構(gòu)就是調(diào)度器,它維護(hù)有存儲M和G的隊列以及調(diào)度器的一些狀態(tài)信息等。
M(Machine):一個M直接關(guān)聯(lián)了一個內(nèi)核線程。
P(processor):代表了M所需的上下文環(huán)境,也是處理用戶級代碼邏輯的處理器。G(Goroutine):其實(shí)本質(zhì)上也是一種輕量級的線程。
它們的關(guān)系如下所示:
介紹:
一個M會關(guān)聯(lián)兩個東西,一個是內(nèi)核線程,一個是可執(zhí)行的進(jìn)程。
一個上下文P會有兩類Goroutine,一類是正在運(yùn)行的,圖中的藍(lán)色G;一類是正在排隊的,圖中灰色G,這個會存儲在該進(jìn)程中的runqueue里面。
這里的上下文P的數(shù)量也表示的是Goroutinue運(yùn)行的數(shù)量,一般設(shè)置為幾個,機(jī)器中就會并發(fā)運(yùn)行幾個。當(dāng)然這里P的數(shù)量是可以設(shè)置的,通過環(huán)境變量GOMAXPROCS的值,或者通過運(yùn)行時調(diào)用函數(shù)runtime.GOMAXPROCS()進(jìn)行設(shè)置,較大值是256。
有了上面的知識,我們知道了Goroutine的一些基本概念,但是我們還是不知道,Go的并發(fā)是如何調(diào)度的。而這一個話題,就需要我們將Goroutine的幾種場景(創(chuàng)建、銷毀和運(yùn)行)做拆分。
1.在執(zhí)行g(shù)o語句之前,我們看下程序都做了哪些準(zhǔn)備,也就是程序的初始化啟動流程是什么樣子的?
上面的代碼,有三個點(diǎn)非常關(guān)鍵,分別是runtime.schedinit,runtime.main,runtime.mstart
Step1: runtime.schedinit:這一步是調(diào)度器的初始化操作,它會設(shè)置GOMAXPROCS的大小,這里的大小不能超過它的上限256,并創(chuàng)建設(shè)置好對應(yīng)數(shù)量的P,當(dāng)然這些P都處于閑置狀態(tài);然后,將這些創(chuàng)建好的P都存放到sched中pidle所關(guān)聯(lián)的閑置列表中。
Step2: 程序會繼續(xù)執(zhí)行runtime.newproc來創(chuàng)建程序的第一個goroutine,而這個goroutine會執(zhí)行runtime.main也就是我們看到的main函數(shù),在這之后main會主動創(chuàng)建一個內(nèi)核線程M,這個M只用來做系統(tǒng)監(jiān)控用,這個內(nèi)核線程與程序中g(shù)oroutinue的調(diào)度有關(guān)系。
Step3:在runtime.mstart之后,程序就開始執(zhí)行了,如果后續(xù)需要創(chuàng)建goroutine,就會調(diào)用go語句來創(chuàng)建。
2.goroutine創(chuàng)建流程是什么樣子的?
在調(diào)用go func()的時候,會調(diào)用runtime.newproc來創(chuàng)建一個goroutine,這個goroutine會新建一個自己的??臻g,同時在G的sched中維護(hù)棧地址與程序計數(shù)器這些信息(備注:這些數(shù)據(jù)在goroutine被調(diào)度的時候會被用到。準(zhǔn)確的說該goroutine在放棄cpu之后,下一次在重新獲取cpu的時候,這些信息會被重新加載到cpu的寄存器中。)
創(chuàng)建好的這個goroutine會被放到,它所對應(yīng)的內(nèi)核線程M所使用的上下文P中的runqueue中。等待調(diào)度器來決定何時取出該goroutine并執(zhí)行,通常調(diào)度是按時間順序被調(diào)度的,這個隊列是一個先進(jìn)先出的隊列。
3.新建的這些goroutine是如何被調(diào)度的呢?
goroutine在創(chuàng)建好了之后,調(diào)度器會決定何時執(zhí)行這個goroutine,這個過程就叫做調(diào)度。
新建好的goroutine,最開始都會存儲在某一個線程M,所關(guān)聯(lián)的上下文P的runqueue中,但是在后續(xù)的調(diào)度中,有些goroutine因?yàn)檎{(diào)用了runtime.gosched,會被放到全局隊列中。
線程M的選擇過程,按照下面的順序執(zhí)行:
1.從M對應(yīng)的P中的runqueue中取出goroutine,來執(zhí)行,沒有的話,執(zhí)行2。
2.從全局隊列里面嘗試取出一個goroutine來執(zhí)行,有的話,執(zhí)行!沒有的話,執(zhí)行3。
3.從其他的線程M的P中,偷出一些goroutine來執(zhí)行,偷失敗了,執(zhí)行4。(備注:這里偷的話,一偷就偷一半,使用的算法叫做work stealing。)
4.線程M發(fā)現(xiàn)無事可做,就去休息了,也就是線程的sleep,它等待被喚醒。
4.運(yùn)行中的goroutine是怎么停止的呢?一旦被停止了的話,那排隊在它后面的goutinue該怎么辦?
講完了goroutine的調(diào)度之后,我們便要考慮一個問題,正在被執(zhí)行的goroutine何時停止,停止了之后會發(fā)生什么?而掛在M對應(yīng)的P后面的runqueue中的goroutine該怎么辦?
情況1:runtime·park
當(dāng)調(diào)用了runtime·park函數(shù)之后,goroutine會被設(shè)置成waiting狀態(tài),線程M會放棄它自身關(guān)聯(lián)的上下文P,而系統(tǒng)會分配一個新的線程M1來接管這個上下文P,(備注:當(dāng)然這里面的M1也有可能是本來就創(chuàng)建好的,處于閑置狀態(tài)中的)。
原來的線程M0則會與上下文斷開連接,M0因?yàn)闊o事可做,就去sleep了,等待下次被喚醒。如下圖所示:
channel的讀寫操作,定時器中,網(wǎng)絡(luò)poll等都有可能park goroutine。
情況2:runtime·gosched
調(diào)用runtime·gosched函數(shù)也可以讓當(dāng)前goroutine放棄cpu,這種情況下會將goroutine設(shè)置成runnable,放置到全局隊列中。備注:這個也就是為什么全局變量的queue里面會有g(shù)oroutine的原因。
5.goroutine被喚醒之后,會做什么?
goroutine處于waiting狀態(tài)的話,在調(diào)用runtime·ready函數(shù)之后,會被喚醒,喚醒的goroutine會被重新放到,M對應(yīng)的上下文所對應(yīng)的runqueue中,等待被調(diào)度。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,的支持。
當(dāng)前名稱:Go語言中g(shù)oroutine的調(diào)度原理是什么-創(chuàng)新互聯(lián)
分享地址:http://muchs.cn/article28/dssscp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、搜索引擎優(yōu)化、服務(wù)器托管、網(wǎng)站策劃、響應(yīng)式網(wǎng)站、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)