goroutine簡介
goroutine是go語言中最為NB的設(shè)計(jì),也是其魅力所在,goroutine的本質(zhì)是協(xié)程,是實(shí)現(xiàn)并行計(jì)算的核心。goroutine使用方式非常的簡單,只需使用go關(guān)鍵字即可啟動(dòng)一個(gè)協(xié)程,并且它是處于異步方式運(yùn)行,你不需要等它運(yùn)行完成以后在執(zhí)行以后的代碼。
go func()//通過go關(guān)鍵字啟動(dòng)一個(gè)協(xié)程來運(yùn)行函數(shù)
go routine的調(diào)度原理和操作系統(tǒng)的線層調(diào)度是比較相似的。這里我們將介紹go routine的相關(guān)知識。
goroutine(有人也稱之為協(xié)程)本質(zhì)上go的用戶級線程的實(shí)現(xiàn),這種用戶級線程是運(yùn)行在內(nèi)核級線程之上。當(dāng)我們在go程序中創(chuàng)建goroutine的時(shí)候,我們的這些routine將會(huì)被分配到不同的內(nèi)核級線程中運(yùn)行。一個(gè)內(nèi)核級線程可能會(huì)負(fù)責(zé)多個(gè)routine的運(yùn)行。而保證這些routine在內(nèi)內(nèi)核級線程安全、公平、高效運(yùn)行的工作,就由調(diào)度器來實(shí)現(xiàn)。
goroutine內(nèi)部原理
概念介紹
在進(jìn)行實(shí)現(xiàn)原理之前,了解下一些關(guān)鍵性術(shù)語的概念。
并發(fā)
一個(gè)cpu上能同時(shí)執(zhí)行多項(xiàng)任務(wù),在很短時(shí)間內(nèi),cpu來回切換任務(wù)執(zhí)行(在某段很短時(shí)間內(nèi)執(zhí)行程序a,然后又迅速得切換到程序b去執(zhí)行),有時(shí)間上的重疊(宏觀上是同時(shí)的,微觀仍是順序執(zhí)行),這樣看起來多個(gè)任務(wù)像是同時(shí)執(zhí)行,這就是并發(fā)。
并行
當(dāng)系統(tǒng)有多個(gè)CPU時(shí),每個(gè)CPU同一時(shí)刻都運(yùn)行任務(wù),互不搶占自己所在的CPU資源,同時(shí)進(jìn)行,稱為并行。
進(jìn)程
cpu在切換程序的時(shí)候,如果不保存上一個(gè)程序的狀態(tài)(也就是我們常說的context--上下文),直接切換下一個(gè)程序,就會(huì)丟失上一個(gè)程序的一系列狀態(tài),于是引入了進(jìn)程這個(gè)概念,用以劃分好程序運(yùn)行時(shí)所需要的資源。因此進(jìn)程就是一個(gè)程序運(yùn)行時(shí)候的所需要的基本資源單位(也可以說是程序運(yùn)行的一個(gè)實(shí)體)。
線程
cpu切換多個(gè)進(jìn)程的時(shí)候,會(huì)花費(fèi)不少的時(shí)間,因?yàn)榍袚Q進(jìn)程需要切換到內(nèi)核態(tài),而每次調(diào)度需要內(nèi)核態(tài)都需要讀取用戶態(tài)的數(shù)據(jù),進(jìn)程一旦多起來,cpu調(diào)度會(huì)消耗一大堆資源,因此引入了線程的概念,線程本身幾乎不占有資源,他們共享進(jìn)程里的資源,內(nèi)核調(diào)度起來不會(huì)那么像進(jìn)程切換那么耗費(fèi)資源。
協(xié)程
協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧。因此,協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài)(即所有局部狀態(tài)的一個(gè)特定組合),每次過程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài),換種說法:進(jìn)入上一次離開時(shí)所處邏輯流的位置。線程和進(jìn)程的操作是由程序觸發(fā)系統(tǒng)接口,最后的執(zhí)行者是系統(tǒng);協(xié)程的操作執(zhí)行者則是用戶自身程序,goroutine也是協(xié)程。
Go調(diào)度的組成
Go的調(diào)度主要有四個(gè)結(jié)構(gòu)組成,分別是:
Go程序的啟動(dòng)過程
創(chuàng)建goroutine:
創(chuàng)建內(nèi)核級線程M
內(nèi)核級線程由go的運(yùn)行時(shí)根據(jù)實(shí)際情況創(chuàng)建,我們無法再go中創(chuàng)建內(nèi)核級線程。那什么時(shí)候回創(chuàng)建內(nèi)核級線程呢?當(dāng)前程序等待運(yùn)行的goroutine數(shù)量達(dá)到一定數(shù)量及存在空閑(為被分配給M)的P的時(shí)候,Go運(yùn)行時(shí)就會(huì)創(chuàng)建一些M,然后將空閑的P分配給新建的內(nèi)核級線程M,接著才是獲取、運(yùn)行g(shù)oroutine。創(chuàng)建M的接口函數(shù)如下:
// 創(chuàng)建M的接口函數(shù) void newm(void (*fn)(void), P *p) // 分配P給M if(m != runtime·m0) {Â acquirep(m->nextp); m->nextp = nil; } // 獲取goroutine并開始運(yùn)行 schedule();
M的運(yùn)行
static void schedule(void) { G *gp; gp = runqget(m->p); if(gp == nil) gp = findrunnable(); // 如果P的類別不止一個(gè)goroutine,且調(diào)度器中有空閑的的P,就喚醒其他內(nèi)核級線程M if (m->p->runqhead != m->p->runqtail runtime·atomicload(runtime·sched.nmspinning) == 0 runtime·atomicload(runtime·sched.npidle) > 0) // TODO: fast atomic wakep(); // 執(zhí)行g(shù)oroutine execute(gp); }
Routine狀態(tài)遷移
前面說的是G,M是怎樣創(chuàng)建的以及什么時(shí)候創(chuàng)建、運(yùn)行。那么goroutine在M是是怎樣進(jìn)行調(diào)度的呢?這個(gè)才是goroutine的調(diào)度核心問題,即上面代碼中的schedule。在說調(diào)度之前,我們必須知道goroutine的狀態(tài)有什么,以及各個(gè)狀態(tài)之間的關(guān)系。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
標(biāo)簽:保定 許昌 德宏 東營 貴州 曲靖 吐魯番 常州
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Go routine調(diào)度詳解》,本文關(guān)鍵詞 routine,調(diào)度,詳解,routine,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。