Golang 的並發模型(一個樹葉過河全靠浪的語言)
我認為這個調度器的原理非常值得我們去研究,因為goroutine是Golang的靈魂嘛????,首先支持Golang(夠浪)調度器有四個重要的結構,分別是M P G Sched
- M 代表著一個內核線程 一個M就是一個內核線程,goroutine就是跑在M之上的
- P 代表著(Processor)處理器 它的主要用途就是用來執行goroutine的,所以它也維護了一個可運行的goroutine隊列,和自由的goroutine隊列,裡面存儲了所有需要它來執行的goroutine。
- G 代表著goroutine 實際的數據結構(就是你封裝的那個方法),並維護者goroutine 需要的棧、程序計數器以及它所在的M等信息。
- Seched 代表著一個調度器 它維護有存儲空閑的M隊列和空閑的P隊列,可運行的G隊列,自由的G隊列以及調度器的一些狀態信息等。
(PS: M P G 的結構都在 /usr/local/go/src/runtime2.go 的文件中。我的go源碼放在了 /usr/local/中。
這篇文章是個基礎入門,我會用一個非常簡單的例子來給大家闡述調度器基本的實現原理。接下來我引用幾張圖片:
每個新的goroutine都會維護這自己的棧,當程序啟動的時候,調度器會創建第一個goroutine,首先,按照圖片上的描述,管理員(Seched)找來土撥鼠(M) 並分配給土撥鼠(M)一個小車(P),土撥鼠開始推車小車拿走一批磚,(我們稱這些磚被放在本地p可運行的G隊列),這個土撥鼠就這麼認真努力的開始完成自己的工作,有一天你在程序中提高一下程序的並發處理能力,創造了很多的磚(G),這時候,這麼多的磚塊(G)已經不夠這些土撥鼠去處理了,於是調度器,就是去倉庫裡面找到空閑的土撥鼠,如果沒有就創造一個土撥鼠,當工廠管理員發現,有個土撥鼠處理的很慢,這個時候就會讓他去處理,但需要讓出小車,調度器會找到一個新的土撥鼠去處理這個推車上的G,當土撥鼠處理完這些G的時候就需要調度器可運行的G隊列總去找,當土撥鼠實在找不到goroutine就去別人的小車中槍走一半,如果多次都搶不到,那麼土撥鼠就丟棄小車回到倉庫裡面睡覺(sleep)去了,如果土撥鼠趕上了系統調用或者網路的IO,那麼土撥鼠就等待他,這時候也不能讓這個土撥鼠來阻礙其他的磚(G)的運行,這時候調度器就會找來新的土撥鼠去完成剩餘的工作,這時候,新的土撥鼠就會搶走車去工作 了,當這個土撥鼠的任務完成了時候發現自己的小車已經被別人搶跑了,這個土撥鼠沒有什麼事情,就回到倉庫裡面睡覺(sleep)去了。
這裡我總結一下:這種調度的演算法叫work stealing 這種演算法適用場景是任務之間的耗時相差比較大,即有的任務很耗時,有的任務很快完成,用這種用演算法很合適;如果任務的耗時很平均則不適合,因為竊取任務也是需要搶佔鎖的,會造成額外的消耗。
上面都使用很簡單的文字來介紹,接下來我會採用Go 1.7的源碼,來描述 上面內容。如果您發現有什麼問題和錯誤,感謝您的指正。(持續更新中。。。)
推薦閱讀: