上週末開始看《Lua程序設計》第二版,目前體會到其中比較有趣的有兩點,一是強大的table數據結構,另外就是coroutine。也許Lua 中的coroutine是一種很好的設計模式,但我初步的體會仍是沒想到其餘語言和場合能很是適合用到coroutine的場景。php
1、簡介
協同程序與線程差很少,也就是一條執行序列,擁有本身獨立的棧,局部變量和指令指針,同時又與其它協同程序共享全局變量和 其它大部分東西。線程與協同程序的主要區別在於,一個具備多線程的程序能夠同時運行幾個線程,而協同程序卻須要彼此協做地運行。就是說,一個具備多個協同 程序的程序在任什麼時候刻只能運行一個協同程序,而且正在運行的協同程序只會在其顯示地掛起時,它的執行纔會暫停。html
如:java
co = coroutine.create(function () for i=1,10 do print("co", i) coroutine.yield() end end)
從主線程調用
coroutine.resume(co)
會依次打印1到10python
2、原理探析
- coroutine建立的所謂的「線程」都不是真正的操做系統的線程,其實是經過保存stack狀態來模擬的。
- 因爲是假的線程,因此切換線程的開銷極小,同時建立線程也是輕量級的,new_thread只是在內存新建了一個stack用於存放新coroutine的變量,也稱做lua_State
LUA_API lua_State *lua_newthread (lua_State *L)編程
- 調用yield()當前線程交出控制權,同時還能夠經過stack返回參數。調用resume的線程(可理解爲主線程)得到返回的參數。
- Lua yield()和Java中的Thread.yield()有點類似,可是區別更大。Java中的yield調用後只是將當前CPU切換到另一個線程,CPU可能隨時會繼續回到線程執行。
- 我更傾向於把Lua中的yield()和resume()和Java中的wait()和notify()來對比。它們表現的行爲基本一致。
- 關於stack實現也可參看Yufeng(Erlang高手)的分析文章 lua coroutine是如何實現的?
3、Why coroutine?
上面對coroutine有個基本的瞭解,所以你們都會象我同樣去想,爲何要用coroutine?先研究下優勢設計模式
- 每一個coroutine有本身私有的stack及局部變量。
- 同一時間只有一個coroutine在執行,無需對全局變量加鎖。
- 順序可控,徹底由程序控制執行的順序。而一般的多線程一旦啓動,它的運行時序是無法預測的,所以一般會給測試全部的狀況帶來困難。因此能用coroutine解決的場合應當優先使用coroutine。
再看缺點,研究coroutine缺點以前,我尋找了一下Lua中爲何實現coroutine的一些說明。在巴西人寫的paper Coroutines in Lua(pdf)中解釋了幾個緣由:數據結構
- Lua是ANSI C實現的,ANSI C並不包含thread的實現,所以若是要在Lua增長thread的支持就要使用操做系統本地的實現,這樣會形成通用的問題。同時也會使Lua變得臃腫。所以Lua選擇了在ANSI C上實現的coroutine。
- Lua主要設計目的之一是給C調用,若是Lua內部又有多線程實現的話會形成C調用狀態的混亂,而只提供coroutine層面的掛起則能夠保持狀態的一致性。
以上這些理由都是基於Lua特殊的緣由而使用的,並非很通用的緣由。咱們也瞭解到,coroutine其實是一種古老的設計模式,它在60年代 就已經定型,可是現代語言不多有重視這個特性,目前能夠舉例的有Windows的fibers, Python的generators多線程
4、Lua coroutine和Erlang
上面優勢有1條沒展開,就是每一個coroutine有本身私有的stack及內存變量空間。所以能夠認爲coroutine和Erlang中的 process是很是類似的。可是coroutine只能同時只有一個在執行,若是能讓他多個同時跑,我以爲就和Erlang很是類似了。模塊化
《Lua程序設計》第二版30.2介紹的一種實現方法,讓多個c threads啓動,而後每一個c thread啓動一個coroutine(相似Erlang process),而後經過stack傳遞變量值(相似Erlang process message),這樣就能夠實現一個相似Erlang的process模型了。因爲coroutine實際上能夠用任何語言實現,那其餘語言應該也可實 現一樣這種設計方法。post
5、Lua其餘
Lua目前主要用在遊戲編程領域,一般的觀點Lua是「膠水語言」。用來把各個模塊化的功能粘合起來。就我目前閱讀的一些代碼來看,C和Lua一般 是混合在一塊兒的,並無明確的邊界。對於我一個外行的眼光看來我分不清哪些是在作C的事情,哪些是在調用Lua。特別是這個「膠水」若是放得太多,系統中 各個模塊的獨立性將會受到影響。好比雲風的這篇Lua 不是 C++也提到,「這屬於過厚的粘合層,是絕對須要拋棄的」。
另外Code@Pig一篇[網遊設計] 一點感想也提到要簡化調用,我總結它的觀點主要兩點:
- 不要存在冗餘的關係,給一個部分負責管理就好。(由Lua/python來管理)
- 粘合層(Lua/python接口)不要過胖,咱們能夠經過引入一個「間接層」來把粘合層作「薄」
雖然Lua的高效和精簡的設計讓人讚譽有加,可是它的性能排名並不高,和Python大體在同一個級別。另外「膠水語言」的定位也妨礙了它在更多領域的發展。