coroutine-協同程序
(C#、Lua、Python支持coroutine特性)
與線程類似:擁有獨立的堆棧,獨立的局部變量,獨立的指令指針,同時與其他協同程序共享全局變量和其他大部分邏輯;
與線程區別:一個具有多個線程的程序,可以同時運行多個線程,而協同程序缺需要彼此協作的運行;在任一指定時刻,只有一個協同程序運行,並且正在運行的協同程序只有明確的被要求掛起時才會被掛起;協同程序有點類似多線程,在等待同一個線程鎖的幾個線程有點類似協作
生產者-消費者協作示例
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 將生產的物品發送給消費者 end end function consumer() while true do local i = receive() -- 從生產者那里得到物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示需要發送的值,值返回以后,就掛起該協同程序 end -- 啟動程序 newProductor = coroutine.create(productor) consumer()
goroutine-搶占式調度機制
(go支持goroutine特性)
調度器:
G-goroutine,每次go調用,都會創建一個G對象;
M-線程, 每次創建一個M的時候,都會有一個底層線程創建,所有G任務,最終還是在M上執行;
P-處理器,每一個運行的M,都必須綁定一個P,就像線程必須在一個CPU核上執行一樣
調用過程
每次go調用的時候,都會: 1. 創建一個G對象,加入到本地隊列或者全局隊列 2. 如果還有空閑的P,則創建一個M 3. M會啟動一個底層線程,循環執行能找到的G任務 4. G任務的執行順序是,先從本地隊列找,本地沒有則從全局隊列找(一次性轉移(全局G個數/P個數)個,再去其它P中找(一次性轉移一半), 5. 以上的G任務執行是按照隊列順序(也就是go調用的順序)執行的。(這個地方是不是覺得很奇怪??) 對於上面的第2-3步,創建一個M,其過程: 1. 先找到一個空閑的P,如果沒有則直接返回,(哈哈,這個地方就保證了進程不會占用超過自己設定的cpu個數) 2. 調用系統api創建線程,不同的操作系統,調用不一樣,其實就是和c語言創建過程是一致的,(windows用的是CreateThread,linux用的是clone系統調用),(*^__^*)嘻嘻…… 3. 然后創建的這個線程里面才是真正做事的,循環執行G任務 那就會有個問題,如果一個系統調用或者G任務執行太長,他就會一直占用這個線程,由於本地隊列的G任務是順序執行的,其它G任務就會阻塞了,怎樣中止長任務的呢?(這個地方我找了好久~o(╯□╰)o) 這樣滴,啟動的時候,會專門創建一個線程sysmon,用來監控和管理,在內部是一個循環: 1. 記錄所有P的G任務計數schedtick,(schedtick會在每執行一個G任務后遞增) 2. 如果檢查到 schedtick一直沒有遞增,說明這個P一直在執行同一個G任務,如果超過一定的時間(10ms),就在這個G任務的棧信息里面加一個標記 3. 然后這個G任務在執行的時候,如果遇到非內聯函數調用,就會檢查一次這個標記,然后中斷自己,把自己加到隊列末尾,執行下一個G 4. O(∩_∩)O哈哈~,如果沒有遇到非內聯函數(有時候正常的小函數會被優化成內聯函數)調用的話,那就慘了,會一直執行這個G任務,直到它自己結束;如果是個死循環,並且GOMAXPROCS=1的話,恭喜你,夯住了!親測,的確如此 對於一個G任務,中斷后的恢復過程: 1. 中斷的時候將寄存器里的棧信息,保存到自己的G對象里面 2. 當再次輪到自己執行時,將自己保存的棧信息復制到寄存器里面,這樣就接着上次之后運行了。
區別
都可以將函數或者語句在獨立的環境中運行;
goroutine支持並行執行,通過channel進行通信,coroutine順序執行,通過讓出和恢復操作來通信(yield、resume)
coroutine 的運行機制屬於協作式任務處理,早期的操作系統要求每一個應用必須遵守操作系統的任務處理規則,應用程序在不需要使用 CPU 時,會主動交出 CPU 使用權。如果開發者無意間或者故意讓應用程序長時間占用 CPU,操作系統也無能為力,表現出來的效果就是計算機很容易失去響應或者死機。
goroutine 屬於搶占式任務處理,已經和現有的多線程和多進程任務處理非常類似。應用程序對 CPU 的控制最終還需要由操作系統來管理,操作系統如果發現一個應用程序長時間大量地占用 CPU,那么用戶有權終止這個任務。
參考:
lua協同程序:https://www.runoob.com/lua/lua-coroutine.html
golang的goroutine調度機制:https://blog.csdn.net/liangzhiyang/article/details/52669851
goroutine和coroutine的區別: http://c.biancheng.net/view/96.html