調(diào)度器 由三方面實體構(gòu)成:
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:國際域名空間、網(wǎng)頁空間、營銷軟件、網(wǎng)站建設(shè)、景泰網(wǎng)站維護、網(wǎng)站推廣。
三者對應(yīng)關(guān)系:
上圖有2個 物理線程 M,每一個 M 都擁有一個上下文(P),每一個也都有一個正在運行的goroutine(G)。
P 的數(shù)量可由 runtime.GOMAXPROCS() 進(jìn)行設(shè)置,它代表了真正的并發(fā)能力,即可有多少個 goroutine 同時運行。
調(diào)度器為什么要維護多個上下文P 呢? 因為當(dāng)一個物理線程 M 被阻塞時,P 可以轉(zhuǎn)而投奔另一個OS線程 M (即 P 帶著 G 連莖拔起,去另一個 M 節(jié)點下運行)。這是 Golang調(diào)度器厲害的地方,也是高并發(fā)能力的保障。
golang學(xué)習(xí)筆記
頻繁創(chuàng)建線程會造成不必要的開銷,所以才有了線程池。在線程池中預(yù)先保存一定數(shù)量的線程,新任務(wù)發(fā)布到任務(wù)隊列,線程池中的線程不斷地從任務(wù)隊列中取出任務(wù)并執(zhí)行,可以有效的減少創(chuàng)建和銷毀帶來的開銷。
過多的線程會導(dǎo)致爭搶cpu資源,且上下文的切換的開銷變大。而工作在用戶態(tài)的協(xié)程能大大減少上下文切換的開銷。協(xié)程調(diào)度器把可運行的協(xié)程逐個調(diào)度到線程中執(zhí)行,同時即時把阻塞的協(xié)程調(diào)度出協(xié)程,從而有效地避免了線程的頻繁切換,達(dá)到了少量線程實現(xiàn)高并發(fā)的效果。
多個協(xié)程分享操作系統(tǒng)分給線程的時間片,從而達(dá)到充分利用CPU的目的,協(xié)程調(diào)度器決定了則決定了協(xié)程運行的順序。每個線程同一時刻只能運行一個協(xié)程。
go調(diào)度模型包含三個實體:
每個處理器維護者一個協(xié)程G的隊列,處理器依次將協(xié)程G調(diào)度到M中執(zhí)行。
每個P會周期性地查看全局隊列中是否有G待運行并將其調(diào)度到M中執(zhí)行,全局隊列中的G主要來自系統(tǒng)調(diào)用中恢復(fù)的G.
如果協(xié)程發(fā)起系統(tǒng)調(diào)用,則整個工作線程M被阻塞,協(xié)程隊列中的其他協(xié)程都會阻塞。
一般情況下M的個數(shù)會略大于P個數(shù),多出來的M將會在G產(chǎn)生系統(tǒng)調(diào)用時發(fā)揮作用。與線程池類似,Go也提供M池子。當(dāng)協(xié)程G1發(fā)起系統(tǒng)掉用時,M1會釋放P,由 M1-P-G1 G2 ... 轉(zhuǎn)變成 M1-G1 , M2會接管P的其他協(xié)程 M2-P-G2 G3 G4... 。
冗余的M可能來源于緩存池,也可能是新建的。
當(dāng)G1結(jié)束系統(tǒng)調(diào)用后,根據(jù)M1是否獲取到P,進(jìn)行不用的處理。
多個處理P維護隊列可能不均衡,導(dǎo)致部分處理器非常繁忙,而其余相對空閑。產(chǎn)生原因是有些協(xié)程自身不斷地派生協(xié)程。
為此Go調(diào)度器提供了工作量竊取策略,當(dāng)某個處理器P沒有需要調(diào)度的協(xié)程時,將從其他處理中偷取協(xié)程,每次偷取一半。
搶占式調(diào)度,是指避免某個協(xié)程長時間執(zhí)行,而阻礙其他協(xié)程被調(diào)度的機制。
調(diào)度器監(jiān)控每個協(xié)程執(zhí)行時間,一旦執(zhí)行時間過長且有其他協(xié)程等待,會把協(xié)程暫停,轉(zhuǎn)而調(diào)度等待的協(xié)程,以達(dá)到類似時間片輪轉(zhuǎn)的效果。比如for循環(huán)會一直占用執(zhí)行權(quán)。
在IO密集型應(yīng)用,GOMAXPROCS大小設(shè)置大一些,獲取性能會更好。
IO密集型會經(jīng)常發(fā)生系統(tǒng)調(diào)用,會有一個新的M啟用或創(chuàng)建,但由于Go調(diào)度器檢測M到被阻塞有一定延遲。如果P數(shù)量多,則P管理協(xié)程隊列會變小。
我這里可以大概給你介紹一下 但對于每一種編程模型要看具體的示例是什么 而且我不可能給你羅列所有的代碼 請諒解 其實我們編程只要盡量站到比較高的層次 很多道理其實你會發(fā)現(xiàn)你已經(jīng)懂了 就多線程來說 我們開始設(shè)想只有兩個線程( 時是不是算數(shù)學(xué)歸納法?)那么如果兩個獨立的線程會發(fā)生什么呢? 當(dāng)一個線程進(jìn)入moniter(也就是說站用一個object) 另一個線程只有等待或返回 而我們把返回就稱為一種模式 這種模式的英文是Balking 這兩個線程可以是有序的執(zhí)行 而不是讓OS來調(diào)度 這時我們要用一個object來調(diào)度 這種模式稱為Scheduler (這個詞及其含義其實OS中就有) 如果這兩個線程同時讀一個資源 我們可以讓他們執(zhí)行 但如果同時寫的話 你閉著眼睛都會知道可能出現(xiàn)問題 這時我們就要用另一種模式(Read/Write Lock) 如果一個線程是為另一個線程服務(wù)的話 比如IE中負(fù)責(zé)數(shù)據(jù)傳輸?shù)木€程和界面顯示的線程 當(dāng)一個圖片沒有傳完時 另一個線程就無法顯示 至少是部分沒有傳完 那么這時我們要用一個模式稱為生產(chǎn)者和消費者 英文是Producer Consumer 兩個線程的消亡也可以不是完全又OS來控制的 這時我們需要給出一個條件 使得每個線程在符合條件是才消亡 也就是有序的消亡 我們稱為Two Phase Termination 那么有這 個線程模型 基本上可以用到大多數(shù)編程任務(wù)中 我需要指出的三點是 從高層次上我們可以再驗證是否含蓋了所有的情況 其實模式不是完全固定的或者說象定律一樣 而模式可以為不同的情況進(jìn)行適當(dāng) 的調(diào)整和組合 目的是為了簡潔和高效 學(xué)習(xí)模式是為了具備更好的分析問題的能力 而似乎這些來自西方的技術(shù) 并且是目前的 我們有沒有呢?其實我個人有個大膽的推測 我認(rèn)為我們祖先的《孫子兵法》就是很好設(shè)計模式 因為它符合設(shè)計模式需要的基本特征 就是在特定的條件下 用某種特定的方式合理且高效的解決問題 只不過一是用在軍事上 二是完備性方面我們還沒研究 但我認(rèn)為我們至少沒有很好的擴展和進(jìn)行類比式的應(yīng)用 否則今天可能是我們中國人教外國人什么是設(shè)計模式 類比的方法實際上是發(fā)明或發(fā)現(xiàn)的常用方法 不知能否讓你感覺到其實外國的技術(shù)并不是那么的神秘 也許我們從自身的文化當(dāng)中挖掘出的東西太少了 P S 更深一步的研究你可以參考Doug Lea lishixinzhi/Article/program/Java/gj/201311/27428
線程:
多線程是為了解決CPU利用率的問題,線程則是為了減少上下文切換時的開銷,進(jìn)程和線程在Linux中沒有本質(zhì)區(qū)別,最大的不同就是進(jìn)程有自己獨立的內(nèi)存空間,而線程是共享內(nèi)存空間。
在進(jìn)程切換時需要轉(zhuǎn)換內(nèi)存地址空間,而線程切換沒有這個動作,所以線程切換比進(jìn)程切換代價要小得多。
協(xié)程:
想要簡單,又要性能高,協(xié)程就可以達(dá)到我們的目的,它是用戶視角的一種抽象,操作系統(tǒng)并沒有這個概念,主要思想是在用戶態(tài)實現(xiàn)調(diào)度算法,用少量線程完成大量任務(wù)的調(diào)度。
Goroutine是GO語言實現(xiàn)的協(xié)程,其特點是在語言層面就支持,使用起來十分方便,它的核心是MPG調(diào)度模型:M即內(nèi)核線程;P即處理器,用來執(zhí)行Goroutine,它維護了本地可運行隊列;G即Goroutine,代碼和數(shù)據(jù)結(jié)構(gòu);S及調(diào)度器,維護M和P的信息。
內(nèi)核線程(Kernel-Level Thread ,KLT)
輕量級進(jìn)程(Light Weight Process,LWP):輕量級進(jìn)程就是我們通常意義上所講的線程,由于每個輕量級進(jìn)程都由一個內(nèi)核線程支持,因此只有先支持內(nèi)核線程,才能有輕量級進(jìn)程
用戶線程與系統(tǒng)線程一一對應(yīng),用戶線程執(zhí)行如lo操作的系統(tǒng)調(diào)用時,來回切換操作開銷相對比較大
多個用戶線程對應(yīng)一個內(nèi)核線程,當(dāng)內(nèi)核線程對應(yīng)的一個用戶線程被阻塞掛起時候,其他用戶線程也阻塞不能執(zhí)行了。
多對多模型是可以充分利用多核CPU提升運行效能的
go線程模型包含三個概念:內(nèi)核線程(M),goroutine(G),G的上下文環(huán)境(P);
GMP模型是goalng特有的。
P與M一般是一一對應(yīng)的。P(上下文)管理著一組G(goroutine)掛載在M(內(nèi)核線程)上運行,圖中左邊藍(lán)色為正在執(zhí)行狀態(tài)的goroutine,右邊為待執(zhí)行狀態(tài)的goroutiine隊列。P的數(shù)量由環(huán)境變量GOMAXPROCS的值或程序運行runtime.GOMAXPROCS()進(jìn)行設(shè)置。
當(dāng)一個os線程在執(zhí)行M1一個G1發(fā)生阻塞時,調(diào)度器讓M1拋棄P,等待G1返回,然后另起一個M2接收P來執(zhí)行剩下的goroutine隊列(G2、G3...),這是golang調(diào)度器厲害的地方,可以保證有足夠的線程來運行剩下所有的goroutine。
當(dāng)G1結(jié)束后,M1會重新拿回P來完成,如果拿不到就丟到全局runqueue中,然后自己放到線程池或轉(zhuǎn)入休眠狀態(tài)??臻e的上下文P會周期性的檢查全局runqueue上的goroutine,并且執(zhí)行它。
另一種情況就是當(dāng)有些P1太閑而其他P2很忙碌的時候,會從其他上下文P2拿一些G來執(zhí)行。
詳細(xì)可以翻看下方第一個參考鏈接,寫得真好。
最后用大佬的總結(jié)來做最后的收尾————
Go語言運行時,通過核心元素G,M,P 和 自己的調(diào)度器,實現(xiàn)了自己的并發(fā)線程模型。調(diào)度器通過對G,M,P的調(diào)度實現(xiàn)了兩級線程模型中操作系統(tǒng)內(nèi)核之外的調(diào)度任務(wù)。整個調(diào)度過程中會在多種時機去觸發(fā)最核心的步驟 “一整輪調(diào)度”,而一整輪調(diào)度中最關(guān)鍵的部分在“全力查找可運行G”,它保證了M的高效運行(換句話說就是充分使用了計算機的物理資源),一整輪調(diào)度中還會涉及到M的啟用停止。最后別忘了,還有一個與Go程序生命周期相同的系統(tǒng)監(jiān)測任務(wù)來進(jìn)行一些輔助性的工作。
淺析Golang的線程模型與調(diào)度器
Golang CSP并發(fā)模型
Golang線程模型
Go的CSP并發(fā)模型
Go實現(xiàn)了兩種并發(fā)形式。第一種是大家普遍認(rèn)知的:多線程共享內(nèi)存。其實就是Java或者C++等語言中的多線程開發(fā)。另外一種是Go語言特有的,也是Go語言推薦的:CSP(communicating sequential processes)并發(fā)模型。
CSP 是 Communicating Sequential Process 的簡稱,中文可以叫做通信順序進(jìn)程,是一種并發(fā)編程模型,由 Tony Hoare 于 1977 年提出。簡單來說,CSP 模型由并發(fā)執(zhí)行的實體(線程或者進(jìn)程)所組成,實體之間通過發(fā)送消息進(jìn)行通信,這里發(fā)送消息時使用的就是通道,或者叫 channel。CSP 模型的關(guān)鍵是關(guān)注 channel,而不關(guān)注發(fā)送消息的實體。 Go 語言實現(xiàn)了 CSP 部分理論 。
“ 不要以共享內(nèi)存的方式來通信,相反, 要通過通信來共享內(nèi)存?!?/p>
Go的CSP并發(fā)模型,是通過 goroutine和channel 來實現(xiàn)的。
goroutine 是Go語言中并發(fā)的執(zhí)行單位。其實就是協(xié)程。
channel是Go語言中各個并發(fā)結(jié)構(gòu)體(goroutine)之前的通信機制。 通俗的講,就是各個goroutine之間通信的”管道“,有點類似于Linux中的管道。
Channel
Goroutine