golang這個新興的語言,最關鍵的就在於goroutine,而goroutine的排程又是golang的核心。可以說,沒有goroutine,那麼這個語言就會毫無意義,沒有發明的必要。為了能夠更好的寫出高質量的**,最近學習了goroutine的排程,收穫良多。寫篇文章總結記錄一下。
analysis of the go runtime scheduler 一篇分析goroutine的**,英文的,其實這篇**寫遠比很多部落格來的通俗易懂,值得一看。
how goroutines work這篇部落格簡略寫goroutine和執行緒的區別,文章底部也有更進一步閱讀參考的鏈結
goroutine與排程器**並茂的講了goroutine排程的演算法。值得一看。
goroutine的建立是不需要的很多記憶體的,大約2kb棧空間而已,而乙個執行緒一建立就要約1mb記憶體,乙個伺服器乙個請求建立乙個goroutine而沒有任何問題,而乙個請求建立乙個執行緒則會導致outofmemoryerror,任何語言用多執行緒來實現併發都會遇到這個問題。
執行緒的建立與銷毀代價是比較大的,所以往往弄乙個執行緒池來解決這個問題。而goroutine的建立銷毀都比較cheap, 當然,其實可以看成go已經實現了執行緒池。
當執行緒阻塞,其他的執行緒必須被排程過來被阻塞執行緒的位置,當執行緒切換,排程器需要儲存所有的暫存器,通用暫存器、程式計數器、棧指標還有其他一些亂七八槽的暫存器,這樣的切換代價是很高的,尤其是在頻繁進行執行緒切換的情況下。
而goroutine的切換代價就很低了,只需要儲存3個暫存器:程式計數暫存器、棧指標以及dx。所以,goroutine排程的切換代價以及時間是可以忽略不計的。
runtime管理排程著所有的goroutine,從出生到死亡。它會分配一些執行緒,採用多路復用的策略,來執行goroutine。在任何時候,乙個執行緒會執行乙個goroutine,如果這個goroutine阻塞了,這個goroutine就會被換出,這個執行緒就會執行其他的goroutine。因為goroutine是合作式的排程(這裡我也不太明白),那麼如果乙個goroutine不停的迴圈就會餓死其他的在這個執行緒上的goroutine。在go1.2中,這個問題已經有所緩解,當進入乙個函式後,runtime會不定時的啟用排程器,所以乙個沒有內聯函式的迴圈會被取代。
goroutine是輕量級的,並且以下原因中不會造成執行緒阻塞。
1. 網路輸入
2. sleeping
3. channel 操作
4. 通過sync包的阻塞操作
儘管你不能直接控制runtime創造執行緒的數量,但是你可以設定系統使用的處理器的核的數量。通過設定gomaxprocs,你就可以增加或減少核的數量來改善你程式的效能了。
和其他語言一樣,避免同時操作共享的記憶體是很重要的,最好的策略是用channel來實現共享,do not cmmunicate by sharing memory; instead, share memory by communicating.
Goroutine的排程模型
當前有兩個p,各自繫結了乙個m,每個p上掛了乙個本地goroutine佇列,也有乙個全域性goroutine佇列。流程 每次使用go關鍵字宣告時,乙個g物件被建立並加入到本地g佇列或者全域性g佇列。檢查是否有空閒的p 處理器 若有那麼建立乙個m 若有正在sleep的m那麼直接喚醒它 與其繫結,然後這...
弄懂goroutine排程原理
golang語言作者rob pike說,goroutine是乙個與其他goroutines 併發執行在同一位址空間的go函式或方法。乙個執行的程式由乙個或更多個goroutine組成。它與執行緒 協程 程序等不同。它是乙個goroutine 簡單的說就是golang自己實現了協程並叫做gorunti...
golang的goroutine排程機制
感覺豁然開朗,受益匪淺 去繁就簡,再加上自己的一些理解,整理了一下 排程器 主要基於三個基本物件上,g,m,p 定義在原始碼的src runtime runtime.h檔案中 1.g代表乙個goroutine物件,每次go呼叫的時候,都會建立乙個g物件 2.m代表乙個執行緒,每次建立乙個m的時候,都...