Lua coroutine 不一样的多线程编程思路
上周末開(kāi)始看《Lua程序設(shè)計(jì)》第二版,目前體會(huì)到其中比較有趣的有兩點(diǎn),一是強(qiáng)大的table數(shù)據(jù)結(jié)構(gòu),另外就是coroutine。也許Lua中的coroutine是一種很好的設(shè)計(jì)模式,但我初步的體會(huì)還是沒(méi)想到其他語(yǔ)言和場(chǎng)合能非常適合用到coroutine的場(chǎng)景。
一、簡(jiǎn)介
協(xié)同程序與線(xiàn)程差不多,也就是一條執(zhí)行序列,擁有自己獨(dú)立的棧,局部變量和指令指針,同時(shí)又與其它協(xié)同程序共享全局變量和其它大部分東西。線(xiàn)程與協(xié)同程序的主要區(qū)別在于,一個(gè)具有多線(xiàn)程的程序可以同時(shí)運(yùn)行幾個(gè)線(xiàn)程,而協(xié)同程序卻需要彼此協(xié)作地運(yùn)行。就是說(shuō),一個(gè)具有多個(gè)協(xié)同程序的程序在任何時(shí)刻只能運(yùn)行一個(gè)協(xié)同程序,并且正在運(yùn)行的協(xié)同程序只會(huì)在其顯示地掛起時(shí),它的執(zhí)行才會(huì)暫停。
如:
co = coroutine.create(function () for i=1,10 do print("co", i) coroutine.yield() end end)從主線(xiàn)程調(diào)用
coroutine.resume(co)
會(huì)依次打印1到10
二、原理探析
- coroutine創(chuàng)建的所謂的“線(xiàn)程”都不是真正的操作系統(tǒng)的線(xiàn)程,實(shí)際上是通過(guò)保存stack狀態(tài)來(lái)模擬的。
- 由于是假的線(xiàn)程,所以切換線(xiàn)程的開(kāi)銷(xiāo)極小,同時(shí)創(chuàng)建線(xiàn)程也是輕量級(jí)的,new_thread只是在內(nèi)存新建了一個(gè)stack用于存放新coroutine的變量,也稱(chēng)作lua_State
LUA_API lua_State *lua_newthread (lua_State *L)
- 調(diào)用yield()當(dāng)前線(xiàn)程交出控制權(quán),同時(shí)還可以通過(guò)stack返回參數(shù)。調(diào)用resume的線(xiàn)程(可理解為主線(xiàn)程)獲得返回的參數(shù)。
- Lua yield()和Java中的Thread.yield()有點(diǎn)相似,但是區(qū)別更大。Java中的yield調(diào)用后只是將當(dāng)前CPU切換到另外一個(gè)線(xiàn)程,CPU可能隨時(shí)會(huì)繼續(xù)回到線(xiàn)程執(zhí)行。
- 我更傾向于把Lua中的yield()和resume()和Java中的wait()和notify()來(lái)對(duì)比。它們表現(xiàn)的行為基本一致。
- 關(guān)于stack實(shí)現(xiàn)也可參看Yufeng(Erlang高手)的分析文章 lua coroutine是如何實(shí)現(xiàn)的?
三、Why coroutine?
上面對(duì)coroutine有個(gè)基本的了解,因此大家都會(huì)象我一樣去想,為什么要用coroutine?先研究下優(yōu)點(diǎn)
- 每個(gè)coroutine有自己私有的stack及局部變量。
- 同一時(shí)間只有一個(gè)coroutine在執(zhí)行,無(wú)需對(duì)全局變量加鎖。
- 順序可控,完全由程序控制執(zhí)行的順序。而通常的多線(xiàn)程一旦啟動(dòng),它的運(yùn)行時(shí)序是沒(méi)法預(yù)測(cè)的,因此通常會(huì)給測(cè)試所有的情況帶來(lái)困難。所以能用coroutine解決的場(chǎng)合應(yīng)當(dāng)優(yōu)先使用coroutine。
再看缺點(diǎn),研究coroutine缺點(diǎn)之前,我尋找了一下Lua中為什么實(shí)現(xiàn)coroutine的一些說(shuō)明。在巴西人寫(xiě)的paper Coroutines in Lua(pdf)中解釋了幾個(gè)原因:
- Lua是ANSI C實(shí)現(xiàn)的,ANSI C并不包含thread的實(shí)現(xiàn),因此如果要在Lua增加thread的支持就要使用操作系統(tǒng)本地的實(shí)現(xiàn),這樣會(huì)造成通用的問(wèn)題。同時(shí)也會(huì)使Lua變得臃腫。因此Lua選擇了在ANSI C上實(shí)現(xiàn)的coroutine。
- Lua主要設(shè)計(jì)目的之一是給C調(diào)用,如果Lua內(nèi)部又有多線(xiàn)程實(shí)現(xiàn)的話(huà)會(huì)造成C調(diào)用狀態(tài)的混亂,而只提供coroutine層面的掛起則可以保持狀態(tài)的一致性。
以上這些理由都是基于Lua特殊的原因而使用的,并不是很通用的原因。我們也了解到,coroutine實(shí)際上是一種古老的設(shè)計(jì)模式,它在60年代就已經(jīng)定型,但是現(xiàn)代語(yǔ)言很少有重視這個(gè)特性,目前可以舉例的有Windows的fibers, Python的generators
四、Lua coroutine和Erlang
上面優(yōu)點(diǎn)有1條沒(méi)展開(kāi),就是每個(gè)coroutine有自己私有的stack及內(nèi)存變量空間。因此可以認(rèn)為coroutine和Erlang中的process是非常相似的。但是coroutine只能同時(shí)只有一個(gè)在執(zhí)行,如果能讓他多個(gè)同時(shí)跑,我覺(jué)得就和Erlang非常相似了。
《Lua程序設(shè)計(jì)》第二版30.2介紹的一種實(shí)現(xiàn)方法,讓多個(gè)c threads啟動(dòng),然后每個(gè)c thread啟動(dòng)一個(gè)coroutine(類(lèi)似Erlang process),然后通過(guò)stack傳遞變量值(類(lèi)似Erlang process message),這樣就可以實(shí)現(xiàn)一個(gè)類(lèi)似Erlang的process模型了。由于coroutine實(shí)際上可以用任何語(yǔ)言實(shí)現(xiàn),那其他語(yǔ)言應(yīng)該也可實(shí)現(xiàn)同樣這種設(shè)計(jì)方法。
五、Lua其他
Lua目前主要用在游戲編程領(lǐng)域,通常的觀點(diǎn)Lua是“膠水語(yǔ)言”。用來(lái)把各個(gè)模塊化的功能粘合起來(lái)。就我目前閱讀的一些代碼來(lái)看,C和Lua通常是混合在一起的,并沒(méi)有明確的邊界。對(duì)于我一個(gè)外行的眼光看來(lái)我分不清哪些是在做C的事情,哪些是在調(diào)用Lua。特別是這個(gè)“膠水”如果放得太多,系統(tǒng)中各個(gè)模塊的獨(dú)立性將會(huì)受到影響。比如云風(fēng)的這篇Lua 不是 C++也提到,“這屬于過(guò)厚的粘合層,是絕對(duì)需要拋棄的”。
另外Code@Pig一篇[網(wǎng)游設(shè)計(jì)] 一點(diǎn)感想也提到要簡(jiǎn)化調(diào)用,我總結(jié)它的觀點(diǎn)主要兩點(diǎn):
雖然Lua的高效和精簡(jiǎn)的設(shè)計(jì)讓人贊譽(yù)有加,但是它的性能排名并不高,和Python大致在同一個(gè)級(jí)別。另外“膠水語(yǔ)言”的定位也妨礙了它在更多領(lǐng)域的發(fā)展。
轉(zhuǎn)載于:https://blog.51cto.com/timyang/307675
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Lua coroutine 不一样的多线程编程思路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GridView、Repeater合并单
- 下一篇: INNODB在裸设备上的性能简单测试