Lua所支持的協程全稱被稱做協同式多線程(collaborative multithreading)。Lua爲每一個coroutine提供一個獨立的運行線路。然而和多線程不一樣的地方就是,coroutine只有在顯式調用yield函數後才被掛起,同一時間內只有一個協程正在運行。python
Lua將它的協程函數都放進了coroutine這個表裏,其中主要的函數以下數組
co = coroutine.create(function()
print("co", coroutine.yield())
end)多線程
coroutine.resume(co)
coroutine.resume(co , "oo" , "xx")異步
輸出結果是:co oo xx () --[爲什麼第一個resume沒有任何輸出呢? 答案是,yield沒有返回,print就根本還沒運行。]函數
4)當一個coroutine結束的時候,main函數的全部返回值都被返回給resume:oop
co = coroutine.create(function()
return "ok", "no"
end)
print(coroutine.resume(co))lua
輸出結果是:true ok nospa
協程的用途最明顯的地方是須要訪問某個異步的功能時,C語言常採用回調的方法:當異步完成時,回調腳本的一個已知的函數。若是程序執行到異步點時,跳回,當異步完成後,再回到跳回點繼續執行。個人理解就是協程是把異步過程,看成同步處理( 所以 也可將一些耗時的操做放置在coroutine中進行,也不至於耽擱其餘邏輯的運行)。function foo(a) print("foo", a) -- a[1] = 3 return coroutine.yield(2 * a) end co = coroutine.create(function ( a, b ) print("co-body_01", a, b) local r = foo(a + 1) print("co-body_02", r) local r, s = coroutine.yield(a + b, a - b) print("co-body_03", r, s) return b, "end" end) print("---main---", coroutine.resume(co, 1, 10)) print("---main---", coroutine.resume(co, "r7")) print("---main---", coroutine.resume(co, "x", "y")) print("---main---", coroutine.resume(co, "x", "y"))
運行結果以下:操作系統
co-body_01 1 10 foo 2 ---main--- true 4 co-body_02 r7 ---main--- true 11 -9 co-body_03 x y ---main--- true 10 end ---main--- false cannot resume dead coroutine
假若將「-- a[1] = 3」 這一行註釋打開,運行則是這樣的:.net
co-body_01 1 10 foo 2 main false D:\UserProfiles\Jeff\Desktop\t_corotine.lua:13: attempt to index local 'a' (a number value) main false cannot resume dead coroutine main false cannot resume dead coroutine main false cannot resume dead coroutine [Finished in 0.1s]
corotine如此這般做用,也使得有些童鞋能夠將該功用 看成Xpcall抑或是pcall使用;將易出錯的代碼寫在協程內,即使出錯也不會是的程序崩潰;
(二)coroutine的和callback的比較
coroutine常常被用來和callback進行比較,由於一般來講,coroutine和callback能夠實現相同的功能,即異步通訊,好比說下面的這個例子:
bob.walkto(jane) bob.say("hello") jane.say("hello")
看起來好像是對的,但實際上因爲這幾個動做walkto,say都是須要必定時間才能作完的
,因此這段程序若是這樣寫的話,就會致使bob一邊走一邊對jane說hello,而後在同時jane也對bob說hello,致使整個流程十分混亂。
若是使用回調來實現的話,代碼示例以下:
bob.walto(function ( ) bob.say(function ( ) jane.say("hello") end,"hello") end, jane)
即walto函數回調say函數,say函數再回調下一個say函數,這樣回調看起來十分混亂,讓人沒法一下看出這段代碼的意義.
若是用coroutine的話,可使用以下寫法:
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),使其返回目前正在執行的協程中。
可是,上述代碼中有許多重複的地方,因此能夠經過將封裝的方式將重複代碼封裝起來:
function runAsyncFunc( func, ... ) local current = coroutine.running func(function ( ) coroutine.resume(current) end, ...) coroutine.yield() end co = coroutine.create(function ( ) runAsyncFunc(bob.walkto, jane) runAsyncFunc(bob.say, "hello") jane.say("hello") end) coroutine.resume(co) coroutine.resume(co) coroutine.resume(co)
這樣就不須要改變從前的全部回調函數,便可經過攜程的方式解決異步調用的問題,使得代碼的結構很是清晰。
能夠把迭代器 循環當作是一個特殊的producer-consumer例子:迭代器produce,循環體consume。下面咱們就看一下coroutine爲咱們提供的強大的功能,用coroutine來實現迭代器。
咱們來遍歷一個數組的全排列。先看一下普通的loop實現,代碼以下:
function printResult(a) for i = 1, #a do io.write(a[i], ' ') end io.write('\n') end function permgen(a, n) n = n or #a if n <= 1 then printResult(a) else for i = 1, n do a[n], a[i] = a[i], a[n] permgen(a, n-1) a[n], a[i] = a[i], a[n] end end end permgen({1,2,3})
再看一下迭代器實現,注意比較下代碼的改變的部分:
function printResult(a) for i = 1, #a do io.write(a[i], ' ') end io.write('\n') end function permgen(a, n) n = n or #a if n <= 1 then coroutine.yield(a) else for i = 1, n do a[n], a[i] = a[i], a[n] permgen(a, n-1) a[n], a[i] = a[i], a[n] end end end function permutations(a) local co = coroutine.create(function () permgen(a) end) return function () local code, res = coroutine.resume(co) return res end end for p in permutations({"a", "b", "c"}) do printResult(p) end
permutations 函數使用了一個Lua中的常規模式,將在函數中去resume一個對應的coroutine進行封裝。Lua對這種模式提供了一個函數coroutine.wap 。跟create 同樣,wrap 建立一個新的coroutine ,可是並不返回給coroutine,而是返回一個函數,調用這個函數,對應的coroutine就被喚醒去運行。跟原來的resume 不一樣的是,該函數不會返回errcode做爲第一個返回值,一旦有error發生,就退出了(相似C語言的assert)。使用wrap, permutations能夠以下實現:
function permutations (a) return coroutine.wrap(function () permgen(a) end) end
wrap 比create 跟簡單,它實在的返回了咱們最須要的東西:一個能夠喚醒對應coroutine的函數。 可是不夠靈活。沒有辦法去檢查wrap 建立的coroutine的status, 也不能檢查runtime-error(沒有返回errcode,而是直接assert)。