Go游戏服务器开发的一些思考(十):goroutine和coroutine
概要
go語言的特色之一就是goroutine。也就是go協(xié)程。由于協(xié)程這個(gè)東西在go語言之前,用到相對(duì)比較少,大家對(duì)協(xié)程的理解程度不一,或有偏差。比如本人剛接觸goroutine時(shí),就對(duì)其比較畏懼,因?yàn)椴恢浪降资侨绾芜\(yùn)作的。因此有必要深入了解下什么是協(xié)程,它的今生前世,以及工作原理
前世
作為服務(wù)器端程序員,一般來說,都會(huì)使用過、或者自己實(shí)現(xiàn)過 “通用的異步任務(wù)系統(tǒng)” ,來達(dá)成安全方便的多線程使用。通常來講,比較典型的會(huì)是基于actor模型及回調(diào)的方式制定差異。
這里我們主要來考察下其不足之處。下面簡(jiǎn)單的畫一下 任務(wù)對(duì)象和線程間的關(guān)系:
------- ------- -------| task1 | | task2 | .... | taskn | => thread1------- ------- -------------- ------- -------| taska | | taskb | .... | taskz | => thread2------- ------- -------...------- ------- -------| taskA | | taskB | .... | taskZ | => threadn------- ------- -------- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如圖所示,一般會(huì)開n個(gè)線程來處理,每個(gè)任務(wù)按一定的策略被投遞到線程中執(zhí)行,任務(wù)完成后,觸發(fā)異步回調(diào)。
假設(shè) 任務(wù)中,有一定比例的任務(wù)是IO阻礙的。那么線程在執(zhí)行這個(gè)任務(wù)時(shí),會(huì)被掛起。導(dǎo)致后面的任務(wù)也只能等待。
總結(jié)下其不足的地方:
* CPU能力不能達(dá)到完全釋放* IO阻塞任務(wù)的數(shù)量與線程被系統(tǒng)調(diào)度成正比* 任務(wù)完成需要回調(diào)方式,編程上不直觀、比較難受(若沒有前兩條不足,這個(gè)大概也不會(huì)出現(xiàn)。背鍋俠是也...)(ps. 協(xié)程起源于單CPU單線程時(shí)代。要解決的問題,同這里表述的。多線程后,為了榨干CPU處理能力,協(xié)程開始用于多線程系統(tǒng),如這里描述的)
程序員的智慧
那么如何才能把 “異步任務(wù)系統(tǒng)” 做的完美呢。假設(shè)能這樣就好了:
* IO阻塞任務(wù),執(zhí)行到阻塞語句時(shí),系統(tǒng)可以下達(dá)它的IO指令又可以把它拎出來,重新插到任務(wù)隊(duì)列最后。假設(shè)能實(shí)現(xiàn)這樣的效果。那么上述的不足也就不存在了:
* 阻塞的任務(wù)因?yàn)榱喑鰜砹?#xff0c;后續(xù)的任務(wù)可以繼續(xù)歡快的在該線程上跑了 * 阻塞的任務(wù)因?yàn)榱喑鰜砹?#xff0c;線程也不會(huì)被阻塞,也就不會(huì)被系統(tǒng)調(diào)度出去了那么如何才能做到。聰明的程序員很快找到了解決方案:
* 將IO阻塞的API hook掉,換成異步實(shí)現(xiàn)
* 模仿操作系統(tǒng)線程的調(diào)度方法,實(shí)現(xiàn)任務(wù)的切出切進(jìn)
這里點(diǎn)下,為什么需要 “實(shí)現(xiàn)任務(wù)的切出切進(jìn)”。由于把IO阻塞的API hook掉,換成異步實(shí)現(xiàn)。如果讓該任務(wù)繼續(xù)執(zhí)行的話,就會(huì)改變?cè)撊蝿?wù)的流程。因此必須切出去。等再次切進(jìn)來時(shí),檢查IO事件是否已經(jīng)到了。到了則如同 IO阻塞完畢,繼續(xù)執(zhí)行任務(wù)流程。否則再次切出。
今生
于是很多產(chǎn)品出來了。
首先是操作系統(tǒng),推出了用戶態(tài) 上下文切換API
| window | 纖程(Fiber系列API) |
| linux | ucontext系列API |
| 其他 | 大廠直接自己匯編搞定,高級(jí)定制(比如go …) |
然后 很多開源庫。這里介紹幾個(gè)有名的
| boost | boost::context、boost::coroutine。僅跨平臺(tái)提供協(xié)程基礎(chǔ)功能。 |
| libco | 騰訊的協(xié)程庫。明顯的閹割版放出。沒有提供上層封裝使用層代碼 |
| libgo | 魅族的協(xié)程庫。和goroutine使用功能上無限接近。 |
| libtask | goroutine前身。goroutine一出,大家驚呆了,原來封裝的好,也可以這么好用。 |
順便吐槽下,翻下libco、libgo、libtask,會(huì)發(fā)現(xiàn)代碼中只有task。而到了市面上則變成了 協(xié)程(coroutine)。玩起概念來…
goroutine、libgo、libco 比較
| goroutine | 全部hook | 8K開始,動(dòng)態(tài)增加,分段棧實(shí)現(xiàn) | 100% | go代碼太龐大,還沒找到… |
| libco | socket系列API | 可制定大小,共享?xiàng)?shí)現(xiàn) | 最基本的協(xié)程功能 | 代碼量小,比較難看,沒看 |
| libgo | socket系列API | 可制定大小,沒有特殊實(shí)現(xiàn) | 接近100% | 代碼量小,可讀性很高,基本看懂 |
(ps.據(jù)libgo的作者說,libgo協(xié)程棧用了 “os的虛擬內(nèi)存機(jī)制,是動(dòng)態(tài)增長(zhǎng)的”。待考究?jī)?yōu)略 )
C++框架新的可能
C++ libgo庫設(shè)計(jì)的非常棒,還原 goroutine 程度 接近100%(當(dāng)然這里指的是用法。穩(wěn)定性、可靠性還需很多人、產(chǎn)品的檢驗(yàn))。因此讓C++引擎具備實(shí)用的cocoutine系統(tǒng)已經(jīng)是完全可行。
總結(jié)
以上是生活随笔為你收集整理的Go游戏服务器开发的一些思考(十):goroutine和coroutine的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang基础-chan的select
- 下一篇: Go游戏服务器开发的一些思考(九):Do