Golang 的並發模型(一個樹葉過河全靠浪的語言)

今天我想講的就是,全靠浪的語言Golang(夠浪) 調度器的實現,那麼為什麼Golang要自己實現那,OS內核不是已經有一個線程調度了嘛?之所以Golang會自己去實現,我認為有兩種原因,一是OS上下文切換太過耗時,二是go-scheduler上說 golang的垃圾回收需要在內存有一致的狀態。

我認為這個調度器的原理非常值得我們去研究,因為goroutine是Golang的靈魂嘛????,首先支持Golang(夠浪)調度器有四個重要的結構,分別是M P G Sched

  1. M 代表著一個內核線程 一個M就是一個內核線程,goroutine就是跑在M之上的
  2. P 代表著(Processor)處理器 它的主要用途就是用來執行goroutine的,所以它也維護了一個可運行的goroutine隊列,和自由的goroutine隊列,裡面存儲了所有需要它來執行的goroutine。
  3. G 代表著goroutine 實際的數據結構(就是你封裝的那個方法),並維護者goroutine 需要的棧、程序計數器以及它所在的M等信息。
  4. Seched 代表著一個調度器 它維護有存儲空閑的M隊列和空閑的P隊列,可運行的G隊列,自由的G隊列以及調度器的一些狀態信息等。

(PS: M P G 的結構都在 /usr/local/go/src/runtime2.go 的文件中。我的go源碼放在了 /usr/local/中。

這篇文章是個基礎入門,我會用一個非常簡單的例子來給大家闡述調度器基本的實現原理。接下來我引用幾張圖片:

這張圖啊!真是一語勝千言,這裡我把 「土撥鼠」 代表一個G 「小車」代表 P「磚塊」 代表一個 G

這個特殊的「土撥鼠」 就是這個工廠的管理員,也就是Seched(調度器)

每個新的goroutine都會維護這自己的棧,當程序啟動的時候,調度器會創建第一個goroutine,首先,按照圖片上的描述,管理員(Seched)找來土撥鼠(M) 並分配給土撥鼠(M)一個小車(P),土撥鼠開始推車小車拿走一批磚,(我們稱這些磚被放在本地p可運行的G隊列),這個土撥鼠就這麼認真努力的開始完成自己的工作,有一天你在程序中提高一下程序的並發處理能力,創造了很多的磚(G),這時候,這麼多的磚塊(G)已經不夠這些土撥鼠去處理了,於是調度器,就是去倉庫裡面找到空閑的土撥鼠,如果沒有就創造一個土撥鼠,當工廠管理員發現,有個土撥鼠處理的很慢,這個時候就會讓他去處理,但需要讓出小車,調度器會找到一個新的土撥鼠去處理這個推車上的G,當土撥鼠處理完這些G的時候就需要調度器可運行的G隊列總去找,當土撥鼠實在找不到goroutine就去別人的小車中槍走一半,如果多次都搶不到,那麼土撥鼠就丟棄小車回到倉庫裡面睡覺(sleep)去了,如果土撥鼠趕上了系統調用或者網路的IO,那麼土撥鼠就等待他,這時候也不能讓這個土撥鼠來阻礙其他的磚(G)的運行,這時候調度器就會找來新的土撥鼠去完成剩餘的工作,這時候,新的土撥鼠就會搶走車去工作 了,當這個土撥鼠的任務完成了時候發現自己的小車已經被別人搶跑了,這個土撥鼠沒有什麼事情,就回到倉庫裡面睡覺(sleep)去了。

這裡我總結一下:這種調度的演算法叫work stealing 這種演算法適用場景是任務之間的耗時相差比較大,即有的任務很耗時,有的任務很快完成,用這種用演算法很合適;如果任務的耗時很平均則不適合,因為竊取任務也是需要搶佔鎖的,會造成額外的消耗。

上面都使用很簡單的文字來介紹,接下來我會採用Go 1.7的源碼,來描述 上面內容。如果您發現有什麼問題和錯誤,感謝您的指正。(持續更新中。。。)


推薦閱讀:

九章演算法 | Google面試題:原子計數
圖書館大媽二分稍微升級
ICA-獨立成分分析
對稱的二叉樹

TAG:Go語言 | 演算法 |