Lua中的協程和多線程很類似,每個協程有本身的堆棧,本身的局部變量,能夠經過yield-resume實如今協程間的切換。不一樣之處是:Lua協程是非搶佔式的多線程,必須手動在不一樣的協程間切換,且同一時刻只能有一個協程在運行。而且Lua中的協程沒法在外部將其中止,並且有可能致使程序阻塞。多線程
協同程序(Coroutine):函數
三個狀態:suspended(掛起,協同剛建立完成時或者yield以後)、running(運行)、dead(函數走完後的狀態,這時候不能再從新resume)。spa
coroutine.create(arg):根據一個函數建立一個協同程序,參數爲一個函數線程
coroutine.resume(co):使協同從掛起變爲運行(1)激活coroutine,也就是讓協程函數開始運行;(2)喚醒yield,使掛起的協同接着上次的地方繼續運行。該函數能夠傳入參數設計
coroutine.status(co):查看協同狀態code
coroutine.yield():使正在運行的協同掛起,能夠傳入參數協程
resume函數的兩種用途雖然都是使協同掛起,但仍是有些許差別的,看下面這個例子:blog
coroutineFunc = function (a, b) for i = 1, 10 do print(i, a, b) coroutine.yield() end end co2 = coroutine.create(coroutineFunc) --建立協同程序co2 coroutine.resume(co2, 100, 200) -- 1 100 200 開啓協同,傳入參數用於初始化 coroutine.resume(co2) -- 2 100 200 coroutine.resume(co2, 500, 600) -- 3 100 200 繼續協同,傳入參數無效 co3 = coroutine.create(coroutineFunc) --建立協同程序co3 coroutine.resume(co3, 300, 400) -- 1 300 400 開啓協同,傳入參數用於初始化 coroutine.resume(co3) -- 2 300 400 coroutine.resume(co3) -- 3 300 400
Lua中協同的強大能力,還在於經過resume-yield來交換數據:產品
(1)resume把參數傳給程序(至關於函數的參數調用);io
(2)數據由yield傳遞給resume;
(3)resume的參數傳遞給yield;
(4)協同代碼結束時的返回值,也會傳給resume
協同中的參數傳遞形勢很靈活,必定要注意區分,在啓動coroutine的時候,resume的參數是傳給主程序的;在喚醒yield的時候,參數是傳遞給yield的。看下面這個例子:
co = coroutine.create(function (a, b) print("co", a, b, coroutine.yield()) end) coroutine.resume(co, 1, 2) --沒輸出結果,注意兩個數字參數是傳遞給函數的 coroutine.resume(co, 3, 4, 5) --co 1 2 3 4 5,這裏的兩個數字參數由resume傳遞給yield
Lua的協同稱爲不對稱協同(asymmetric coroutines),指「掛起一個正在執行的協同函數」與「使一個被掛起的協同再次執行的函數」是不一樣的,有些語言提供對稱協同(symmetric coroutines),即便用同一個函數負責「執行與掛起間的狀態切換」。
注意:resume運行在保護模式下,所以,若是協同程序內部存在錯誤,Lua並不會拋出錯誤,而是將錯誤返回給resume函數。
如下是我我的的一點理解:
(1)resume能夠理解爲函數調用,而且能夠傳入參數,激活協同時,參數是傳給程序的,喚醒yield時,參數是傳遞給yield的;
(2)yield就至關因而一個特殊的return語句,只是它只是暫時性的返回(掛起),而且yield能夠像return同樣帶有返回參數,這些參數是傳遞給resume的。
爲了理解上面兩句話的含義,咱們來看一下如何利用Coroutine來解決生產者——消費者問題的簡單實現:
produceFunc = function() while true do local value = io.read() print("produce: ", value) coroutine.yield(value) --返回生產的值 end end consumer = function(p) while true do local status, value = coroutine.resume(p); --喚醒生產者進行生產 print("consume: ", value) end end --消費者驅動的設計,也就是消費者須要產品時找生產者請求,生產者完成生產後提供給消費者 producer = coroutine.create(produceFunc) consumer(producer)
這是一種消費者驅動的設計,咱們能夠看到resume操做的結果是等待一個yield的返回,這很像普通的函數調用,有木有。咱們還能夠在生產消費環節之間加入一箇中間處理的環節(過濾器):
produceFunc = function() while true do local value = io.read() print("produce: ", value) coroutine.yield(value) --返回生產的值 end end filteFunc = function(p) while true do local status, value = coroutine.resume(p); value = value *100 --放大一百倍 coroutine.yield(value) end end consumer = function(f, p) while true do local status, value = coroutine.resume(f, p); --喚醒生產者進行生產 print("consume: ", value) end end --消費者驅動的設計,也就是消費者須要產品時找生產者請求,生產者完成生產後提供給消費者 producer = coroutine.create(produceFunc) filter = coroutine.create(filteFunc) consumer(filter, producer)
能夠看到,咱們在中間過濾器中將生產出的值放大了一百倍。
經過這個例子應該很容易理解coroutine中如何利用resume-yield調用來進行值傳遞了,他們像「調用函數——返回值」同樣的工做,也就是說resume像函數調用同樣使用,yield像return語句同樣使用。coroutine的靈活性也體如今這種經過resume-yield的值傳遞上。