自己對go協(xié)程的理解

前言:

下面是自己看了很多資料后總結(jié)下來的不一定對.因?yàn)槟芰€不夠,還不能夠深入源碼一探究竟.
如果自己哪里不對,希望可以留言.一起討論一下.

進(jìn)程線程協(xié)程的理解

1. 進(jìn)程 :是計(jì)算機(jī)進(jìn)行資源分配的最小單位,上下文開銷很大,進(jìn)程之間數(shù)據(jù)是隔離的.
2. 線程 :是計(jì)算機(jī)調(diào)度的最小單位,是計(jì)算機(jī)運(yùn)行的基本單元.(個(gè)人理解 可以把線程理解成是共享堆的進(jìn)程),上
下文調(diào)度相對于進(jìn)程來說要輕量(因?yàn)榫€程之間調(diào)度不需要切換進(jìn)程資源)
3. 協(xié)程 :是用戶態(tài)的一種輕量的線程,協(xié)程的調(diào)度完全由用戶控制. 不需要內(nèi)核參與,所以速度很快.

go協(xié)程模型和特點(diǎn)

協(xié)作顧名思義是互相協(xié)作的,但是go的協(xié)程不完全是互相協(xié)作的,還會相互搶占.go底層是基于gmp模型實(shí)現(xiàn)的協(xié)程調(diào)度.
M: 系統(tǒng)真正的線程.m的任務(wù)就是想辦法去找到可以運(yùn)行的g.并且運(yùn)行它
P: G的集合,有個(gè)g的鏈表.一般會和m綁定.m不斷運(yùn)行p上面的g.并且p會存儲m上線程的相關(guān)信息
G: go的協(xié)程真正的實(shí)體.代表正在要執(zhí)行的協(xié)程的邏輯和其要運(yùn)行的上下文.

自己對gmp模型的理解(不一定準(zhǔn)確)

大家有沒有覺得m->p->g的關(guān)系其實(shí)就是 計(jì)算機(jī)->進(jìn)程->線程的關(guān)系. 因?yàn)橛?jì)算機(jī)涉及到很多因素的影響,所以對進(jìn)程
線程之間的調(diào)度開銷會很大所以go的作者很有創(chuàng)造性的把這個(gè)過程在用戶態(tài)自己模擬一套.正因?yàn)槿绱艘恍┖唵蔚恼{(diào)度都
不需要用到系統(tǒng)來參與,go表示我自己就可以完成了,所以性能要好的很多!

具體GMP是怎么運(yùn)行的

首先協(xié)程實(shí)現(xiàn)的功能和線程是一樣的,就是讓我們能夠同時(shí)運(yùn)行多個(gè)邏輯的能力(這里指的是并發(fā)不是并行).

1. 啟動(dòng)協(xié)程: go程序內(nèi)部都維護(hù)了一個(gè) gfree的一個(gè)list.每次我們啟動(dòng)一個(gè)新的協(xié)程的時(shí)候 程序不會 
馬上就new一個(gè)g對象.而是先去gfree里找,如果gfree里面有現(xiàn)成的g對象,就會直接拿來用.如果gfree里面
沒有才會去new一個(gè)對象,所有的g運(yùn)行完了都不會直接銷毀.而是放到了gfree里.相當(dāng)于gfree是一個(gè)g的對象池.
總而言之go底層g是復(fù)用的.

2. 協(xié)程綁定p: 當(dāng)協(xié)程被啟動(dòng)后就會優(yōu)先依附于啟動(dòng)自己的m所綁定的p上,這樣很大程度上避免了m與m之間的競爭.
當(dāng)m上的p的g已經(jīng)超過了承受范圍就會放到全局的g的隊(duì)列里.與m綁定的p最多只有256個(gè)g. 

3. m的運(yùn)行: m就是不斷地運(yùn)行依附于自己上的p上的g.為了讓全局的g隊(duì)列里也能夠被調(diào)度到.m每運(yùn)行61個(gè)本地g就會
去全局g隊(duì)列里拿一個(gè)g運(yùn)行.當(dāng)m本地的p沒有g(shù)可以運(yùn)行的時(shí)候也會去全局隊(duì)列里面拿g運(yùn)行.當(dāng)全局隊(duì)列也沒有g(shù)的時(shí)候.
就會去別的m綁定的p上偷一半g來運(yùn)行.如果這個(gè)時(shí)候m還是拿不到g.那么這個(gè)m就會陷入睡眠.值得注意的是睡眠m也是
不會被銷毀的.防止m被頻繁的創(chuàng)建銷毀.

go的協(xié)程 什么時(shí)候調(diào)度的,怎么調(diào)度的

首先我們需要知道go的協(xié)作在什么情況會被調(diào)度(就是別的協(xié)程搶占)

1. 當(dāng)協(xié)程運(yùn)行時(shí)間過長的時(shí)候,那么這個(gè)協(xié)程在調(diào)用非內(nèi)聯(lián)函數(shù)的時(shí)候會被調(diào)度:
正式因?yàn)橛羞@個(gè)特性,go的協(xié)程才具有了搶占性,才讓goroutine不再是單純的協(xié)程. 首先程序會在啟動(dòng)的時(shí)候運(yùn)行一個(gè)
sysmon線程,這個(gè)線程負(fù)責(zé)整個(gè)的調(diào)度具有上帝視角.sysmon會給所有的p都記錄一個(gè)已運(yùn)行的g的數(shù)量.每當(dāng)p運(yùn)行一個(gè)g后
都會加1.如果這個(gè)變量沒有加1,就說明這個(gè)p一直在運(yùn)行同一個(gè)g任務(wù).如果超過了10ms,就給這個(gè)g的棧信息上加個(gè)標(biāo)記.
當(dāng)有了這個(gè)標(biāo)記,這個(gè)g在遇到非內(nèi)聯(lián)函數(shù)的時(shí)候就會把自己中斷掉,移到當(dāng)前p的隊(duì)列尾.

2. 當(dāng)協(xié)程陷入一個(gè)系統(tǒng)調(diào)用.沒有返回的時(shí)候會被調(diào)度
當(dāng)m進(jìn)行系統(tǒng)調(diào)用的時(shí)候,這個(gè)m會被陷入到內(nèi)核態(tài),毫無疑問的被阻塞了.還是sysmon會定期檢測,當(dāng)一個(gè)p上的g執(zhí)行時(shí)間過長
且這個(gè)g還是在進(jìn)行系統(tǒng)調(diào)用.那么sysmon會把p與當(dāng)前m解綁, 然后喚醒一個(gè)之前沒事做的睡眠的m進(jìn)行綁定運(yùn)行.如果沒有
睡眠的m就會重新創(chuàng)建一個(gè)m來綁定,之前的m運(yùn)行著進(jìn)行系統(tǒng)調(diào)用的g返回后就會嘗試找沒有m的p.如果沒有找到,m就會把g放到
全局隊(duì)列,然后自己進(jìn)入睡眠.

3. 當(dāng)協(xié)程被channel阻塞時(shí)會被調(diào)度
這個(gè)不需要多說.當(dāng)g被channel阻塞后,這個(gè)g會被放到相應(yīng)的wait隊(duì)列.該g的狀態(tài)由_Gruning變成_Gwating,g會被別的g的
channel操作喚醒,那么這個(gè)g就會嘗試加入那個(gè)g所在的p的runnext,如果沒有成功,就會加入本地p隊(duì)列,全局隊(duì)列.等待被m運(yùn)行.

4. 當(dāng)協(xié)程進(jìn)行網(wǎng)絡(luò)io阻塞的時(shí)候會被調(diào)度
網(wǎng)絡(luò)io雖然也算是系統(tǒng)調(diào)用,但是go原生的網(wǎng)絡(luò)包底層全是由epoll類似的驅(qū)動(dòng)的,并不走之前系統(tǒng)調(diào)用那一套.sysmon線程會進(jìn)行
epoll_wait,然后對程序每個(gè)socket進(jìn)行標(biāo)記,哪些socket是可寫的,哪些socket是可讀的,然后嘗試喚醒因?yàn)閟ocket不可寫或者
不可讀的g,而g在對socket進(jìn)行讀寫的時(shí)候就判斷sysmon有沒有對其標(biāo)記過,如果沒有標(biāo)記過就進(jìn)入wait狀態(tài),如果標(biāo)記過了就說明可
用就繼續(xù)操作,操作完了,再把這個(gè)標(biāo)記去除了,等待下次標(biāo)記.

總結(jié):

1. GMP 三者能夠隨意綁定解綁,達(dá)到了靈活調(diào)度的目的
2. go后臺運(yùn)行這sysmon線程來進(jìn)行調(diào)度.和上帝一樣.
3. go程序底層有g(shù)的對象池,已經(jīng)對g進(jìn)行了復(fù)用不會被銷毀. 同時(shí)m也是不會被銷毀的.所以從某種意義來講go確實(shí)越運(yùn)行越占內(nèi)存.

參考資料

1. https://juejin.im/post/5b7678f451882533110e8948
2. https://wudaijun.com/2018/01/go-scheduler/
3. https://www.w3cschool.cn/go_internals/go_internals-8h5b282y.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我是在深入學(xué)習(xí) kotlin 時(shí)第一次看到協(xié)程,作為傳統(tǒng)線程模型的進(jìn)化版,雖說協(xié)程這個(gè)概念幾十年前就有了,但是協(xié)程...
    前行的烏龜閱讀 100,477評論 32 182
  • 進(jìn)程時(shí)代 后來,現(xiàn)代化的計(jì)算機(jī)有了操作系統(tǒng),每個(gè)程序都是一個(gè)進(jìn)程,但是操作系統(tǒng)在一段時(shí)間只能運(yùn)行一個(gè)進(jìn)程,直到這個(gè)...
    大學(xué)渣PG閱讀 1,997評論 0 1
  • 如今,微信擁有月活躍用戶8億。 不可否認(rèn),當(dāng)今的微信后臺擁有著強(qiáng)大的并發(fā)能力。 不過, 正如羅馬并非一日建成;微信...
    一凡_44e0閱讀 1,161評論 0 0
  • 進(jìn)程、線程和協(xié)程 進(jìn)程的定義: 進(jìn)程,是計(jì)算機(jī)中已運(yùn)行程序的實(shí)體。程序本身只是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程才...
    星丶雲(yún)閱讀 1,528評論 2 14
  • 閱讀1小時(shí),總計(jì)552小時(shí),第517日。 閱讀《法律的經(jīng)濟(jì)分析》至95% 即美國憲法嚴(yán)禁政府在沒有正當(dāng)法律程序的條...
    龍?zhí)赘缢_克海龍閱讀 192評論 0 0

友情鏈接更多精彩內(nèi)容