一文詳解Golang協(xié)程調(diào)度器scheduler
1. 調(diào)度器scheduler的作用
我們都知道,在Go語言中,程序運(yùn)行的最小單元是gorouines。
然而程序的運(yùn)行最終都是要交給操作系統(tǒng)來執(zhí)行的,以Java為例,Java中的一個(gè)線程對(duì)應(yīng)的就是操作系統(tǒng)中的線程,以此來實(shí)現(xiàn)在操作系統(tǒng)中的運(yùn)行。在Go中,gorouines比線程更輕量級(jí),其與操作系統(tǒng)的線程也不是一一對(duì)應(yīng)的關(guān)系,然而,最終我們想要執(zhí)行程序,還是要借助操作系統(tǒng)的線程來完成,調(diào)度器scheduler的工作就是完成gorouines到操作系統(tǒng)線程的調(diào)度。
2. GMP模型
當(dāng)我們運(yùn)行go fun(){}
時(shí),會(huì)生成一個(gè)g,優(yōu)先放置在創(chuàng)建他的p的本地隊(duì)列中,如果本地隊(duì)列已滿,那么會(huì)放置在全局隊(duì)列中。
g的運(yùn)行需要借助p與m,p是執(zhí)行器,只有獲得p的g才能執(zhí)行,p的執(zhí)行需要掛在m上,m對(duì)應(yīng)的是操作系統(tǒng)中的線程,p的數(shù)量與CPU的核數(shù)相同。
goroutine運(yùn)行所需要的上下文信息都是存放在g的數(shù)據(jù)結(jié)構(gòu)當(dāng)中的,所以g可以依靠任意的p或者m執(zhí)行,而對(duì)于操作系統(tǒng)而言,其并不能看到p與g的調(diào)度過程,這些過程對(duì)于操作系統(tǒng)線程來說都是連續(xù)的,所以省去了線程上下文切換的開銷。
g的數(shù)據(jù)結(jié)構(gòu)如下所示:
type g struct { stack stack // g自己的棧 m *m // 執(zhí)行當(dāng)前g的m sched gobuf // 保存了g的現(xiàn)場(chǎng),goroutine切換時(shí)通過它來恢復(fù) atomicstatus uint32 // g的狀態(tài)Gidle,Grunnable,Grunning,Gsyscall,Gwaiting,Gdead goid int64 schedlink guintptr // 下一個(gè)g, g鏈表 preempt bool //搶占標(biāo)記 lockedm muintptr // 鎖定的M,g中斷恢復(fù)指定M執(zhí)行 gopc uintptr // 創(chuàng)建該goroutine的指令地址 startpc uintptr // goroutine 函數(shù)的指令地址 }
p的數(shù)據(jù)結(jié)構(gòu)如下所示:
type p struct { id int32 status uint32 // 狀態(tài) link puintptr // 下一個(gè)P, P鏈表 m muintptr // 擁有這個(gè)P的M mcache *mcache // P本地runnable狀態(tài)的G隊(duì)列 runqhead uint32 runqtail uint32 runq [256]guintptr runnext guintptr // 一個(gè)比runq優(yōu)先級(jí)更高的runnable G // 狀態(tài)為dead的G鏈表,在獲取G時(shí)會(huì)從這里面獲取 gFree struct { gList n int32 } gcBgMarkWorker guintptr // (atomic) gcw gcWork }
m的數(shù)據(jù)結(jié)構(gòu)如下所示:
type m struct { g0 *g // g0, 每個(gè)M都有自己獨(dú)有的g0 curg *g // 當(dāng)前正在運(yùn)行的g p puintptr // 當(dāng)前用于的p nextp puintptr // 當(dāng)m被喚醒時(shí),首先擁有這個(gè)p id int64 spinning bool // 是否處于自旋 park note alllink *m // on allm schedlink muintptr // 下一個(gè)m, m鏈表 mcache *mcache // 內(nèi)存分配 lockedg guintptr // 和 G 的lockedm對(duì)應(yīng) freelink *m // on sched.freem }
通過gmp模型,我們能解決gorouines到操作系統(tǒng)線程的映射問題,gorouines之間的切換是在用戶態(tài)完成的,在操作系統(tǒng)的視角來看,線程的上下文切換并不頻繁,因此就少了很多陷入內(nèi)核的過程,所以有更好的并發(fā)效果。
3. 調(diào)度機(jī)制
1)work stealing機(jī)制
當(dāng)一個(gè)p上的g執(zhí)行完之后,他會(huì)嘗試從其他的p隊(duì)列中竊取g來執(zhí)行,以減少操作系統(tǒng)線程的切換動(dòng)作。
2)hand off機(jī)制
這個(gè)是針對(duì)m來說的,有的時(shí)候m可能因?yàn)間的信號(hào)調(diào)用而被操作系統(tǒng)阻塞,這個(gè)時(shí)候p就會(huì)掛載去另一個(gè)m繼續(xù)執(zhí)行可以執(zhí)行的g,當(dāng)阻塞的m就緒之后,會(huì)給p發(fā)信號(hào),召喚他回來繼續(xù)進(jìn)行后續(xù)操作。
到此這篇關(guān)于一文詳解Golang協(xié)程調(diào)度器scheduler的文章就介紹到這了,更多相關(guān)Golang scheduler內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入理解Golang之http server的實(shí)現(xiàn)
這篇文章主要介紹了深入理解Golang之http server的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Go可變參數(shù)函數(shù)的實(shí)現(xiàn)
可變參數(shù)函數(shù)是指函數(shù)參數(shù)的某個(gè)參數(shù)可有可無,即這個(gè)參數(shù)的個(gè)數(shù)可以為0會(huì)多個(gè),可變參數(shù)函數(shù)參數(shù)在日常編程中大量使用,本文主要介紹了Go可變參數(shù)函數(shù)的實(shí)現(xiàn),感興趣的可以了解一下2023-12-12go?打包運(yùn)行文件在windows,liunx運(yùn)行
這篇文章主要介紹了go?打包運(yùn)行文件在windows,liunx運(yùn)行的相關(guān)資料,需要的朋友可以參考下2023-11-11解決Go?Json?Unmarshal反序列化丟失數(shù)字精度問題
業(yè)務(wù)會(huì)使用?id生成器?產(chǎn)生的?分布式唯一ID,長度比較長,所以代碼反序列化時(shí),會(huì)出現(xiàn)精度丟失問題,那如何解決呢,下面小編就來和大家詳細(xì)講講2023-08-08Go html/template 模板的使用實(shí)例詳解
這篇文章主要介紹了Go html/template 模板的使用實(shí)例詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05