###coroutine基礎多線程
Lua所支持的協程全稱被稱做協同式多線程(collaborative multithreading)。Lua爲每一個coroutine提供一個獨立的運行線路。然而和多線程不一樣的地方就是,coroutine只有在顯式調用yield函數後才被掛起,同一時間內只有一個協程正在運行。異步
Lua將它的協程函數都放進了coroutine這個表裏,其中主要的函數以下函數
摘取一段雲風的代碼來詳盡解釋協程的工做機制,在這段代碼中,展現了main thread和協程co之間的交互:oop
<!-- lang: lua --> function foo(a) print("foo", a) return coroutine.yield(2 * a) end co = coroutine.create(function ( a, b ) print("co-body", a, b) local r = foo(a + 1) print("co-body", r) local r, s = coroutine.yield(a + b, a - b) print("co-body", r, s) return b, "end" end) print("main", coroutine.resume(co, 1, 10)) print("main", coroutine.resume(co, "r")) print("main", coroutine.resume(co, "x", "y")) print("main", coroutine.resume(co, "x", "y"))
下面是運行結果lua
co-body 1 10 foo 2 main true 4 co-body r main true 11, -9 co-body x y main false 10 end main false cannot resume dead coroutine
###coroutine和subroutine(子例程)的比較spa
子例程的起始處是惟一的入口點,一旦return就完成了子程序的執行,子程序的一個實例只會運行一次。.net
可是協程不同,協程能夠使用yield來切換到其餘協程,而後再經過resume方法重入**(reenter)到上次調用yield的地方,而且把resume的參數當成返回值傳給了要重入(reenter)**的協程。可是coroutine的狀態都沒有被改變,就像一個能夠屢次返回的subroutine
,線程
協程的精妙之處就在於協做
這一律念,下面咱們用生產者和消費者問題來演示一下協程的基本應用。注意:下面的僞碼是用Lua的思想寫的code
var q = queue()
生產者的僞碼協程
loop while q is not full create product add the items to q resume to consumer
消費者的僞碼
loop while q is not empty consume product remove the items from q yield
###coroutine的和callback的比較
coroutine常常被用來和callback進行比較,由於一般來講,coroutine和callback能夠實現相同的功能,即異步通訊,好比說下面的這個例子:
<!-- lang: lua --> bob.walkto(jane) bob.say("hello") jane.say("hello")
看起來好像是對的,但實際上因爲這幾個動做walkto,say都是須要必定時間才能作完的
,因此這段程序若是這樣寫的話,就會致使bob一邊走一邊對jane說hello,而後在同時jane也對bob說hello,致使整個流程十分混亂。
若是使用回調來實現的話,代碼示例以下:
<!-- lang: lua --> bob.walto(function ( ) bob.say(function ( ) jane.say("hello") end,"hello") end, jane)
即walto函數回調say函數,say函數再回調下一個say函數,這樣回調看起來十分混亂,讓人沒法一下看出這段代碼的意義.
若是用coroutine的話,能夠使用以下寫法:
<!-- lang: lua --> co = coroutine.create(function ( ) local current = coroutine.running bob.walto(function ( ) coroutine.resume(current) end, jane) coroutine.yield() bob.say(function ( ) coroutine.resume(current) end, "hello") coroutine.yield() jane.say("hello") end) coroutine.resume(co)
在上述代碼中,一旦一個異步函數被調用,協程就會使用coroutine.yield()方法將該協程暫時懸掛起來,在相應的回調函數中加上coroutine.resume(current),使其返回目前正在執行的協程中。
可是,上述代碼中有許多重複的地方,因此能夠經過將封裝的方式將重複代碼封裝起來
<!-- lang: lua --> function runAsyncFunc( func, ... ) local current = coroutine.running func(function ( ) coroutine.resume(current) end, ...) coroutine.yield() end coroutine.create(function ( ) runAsyncFunc(bob.walkto, jane) runAsyncFunc(bob.say, "hello") jane.say("hello") end) coroutine.resume(co)
這樣就不須要改變從前的全部回調函數,便可經過攜程的方式解決異步調用的問題,使得代碼的結構很是清晰。