Coroutine 協程學習
協程:通過顯式調用 coroutine.create 函數來創建一個協程,把一個函數作爲協程主體來執行。當我們啓動 (resume) 協程時,它開始運行函數體並且直到結束或者讓出控制權 (yield) ;一個協程只有通過顯式調用 yield 函數纔會中斷。以後,我們可以 resume 它,它將會從它停止的地方繼續執行。
一、status:supended、running、dead
二、方法:
-
coroutine.resume() 喚醒:
1、正常退出,coroutine.resume返回true
2、異常發生錯誤,coroutine.resume返回false -
coroutine.yield() 掛起:但yield在協程被掛起時可以返回值,而return只有協程結束纔能有返回值
-
coroutine.create() 創建之後,協程的狀態是掛起的
-
coroutine.wrap方法:不可以使用status和resume方法
-
coroutine.running返回正在跑的coroutine
-
coroutine.status(coroutine.running(co))
三、方法學習:
1、resume與yield相互傳數據
local function yieldFun(x, y)
coroutine.yield(x * y, x / y) -- 協程掛起時,返回resume
return x * y, x - y -- 協程結束時,返回resume
end
local co2 = coroutine.create(yieldFun)
print(coroutine.resume(co2, 10, 20)) -- 200 0.5
print(coroutine.resume(co2, 20, 40)) -- 200 -10 (20 40 未傳入)
print(coroutine.resume(co2))
resume第一次發的 10 20 發給的yield所在的函數yieldFun作參數,第二次發給的yield的返回值,因爲這裏沒有兩次yield所以傳不進去
yield發給的當前這次resume作返回值,return返回給第二個resume
2、wrap方法
local co1 = coroutine.wrap(function(input)
print("inpput:", input)
print("yield", coroutine.yield("sus"))
return "end"
end)
--print(coroutine.resume(co1)) -- error ; wrap()不能直接使用resume
print("step1 : ", co1("123"))
print("step2 : ", co1("456"))
3、開另外一個協程
local co4 = coroutine.create(function()
print("co4 start")
print(coroutine.running())
end)
local co3 = coroutine.create(function()
print("co3 start")
coroutine.resume(co4)
coroutine.yield(co3)
end)
coroutine.resume(co3)
4、綜合:其實做一個在死循環裏面相互調用的情況,其中,resume向yield傳了兩次不一樣的值,第一次給funYield做參數,後面五次發給coroutine.yield(t)的返回值,判斷次數id來控制yield調用,然後判斷status跳出
function funYield(t)
print("fun")
while t >= 0 do
print("funYield : ", t)
print("coroutine:", coroutine.running(co), coroutine.status(coroutine.running(co)))
t = coroutine.yield(t)
end
return "end"
end
local co = coroutine.create(funYield)
print(coroutine.running(co))
print(coroutine.resume(co, 10))
id = 5
while true do
print(coroutine.resume(co, id))
print("status:", coroutine.status(co))
if "dead" == coroutine.status(co) then
break
end
id = id - 1
end
print(coroutine.resume(co))
print(coroutine.running(co))
5、根據4的情況+了閉包的使用,主要是全局變量換成局部變量,學習了閉包,在閉包創建時,它調用luaF_findupval時,會去找upvalue的鏈表裏面有複用的upvalue(否則新建),主要是非局部變量upvalue,問題是如何申請一個揹包,fun indexFun 裏面返回的是 fun 匿名函數 ,indexFun 引起了匿名函數 的局部變量。
function indexFun()
local x = 3
return function()
x = x - 1
return x
end
end
function funYield(t)
print("fun")
while t >= 0 do
print("funYield : ", t)
print("coroutine:", coroutine.running(co), coroutine.status(coroutine.running(co)))
coroutine.yield(t)
end
return "end"
end
local co = coroutine.create(funYield)
print(coroutine.running(co))
print(coroutine.resume(co, 3))
local tempFun = indexFun()
while true do
local id = tempFun()
print("index:", id)
print(coroutine.resume(co, id))
print("status:", coroutine.status(co))
if "dead" == coroutine.status(co) then
break
end
if id <= 0 then
break
end
end
print(coroutine.resume(co))
print(coroutine.running(co))
還有個尾調用的概念:在f(x)函數結尾調用g(x)【類似goto】無論調用多少次都不會導致棧溢出,g(x)結束不返回
閉包學習參考:https://www.cnblogs.com/freebird92/p/6764357.html
四、備註:
skynet與正常的coroutine:
1、如果skynet的coroutine與正常的coroutine混用 所有的skynet阻塞接口觸發yield
2、skynet狀態加了一個類型 :blocked(其實是normal的一個特例) 即當前coroutine被底層框架掛起,但不可以由引用框架resume 喚起
3、而在使用skynet的過程中,一般不經常直接使用coroutine模塊,skynet本身提供了skynet.fork()來創建協程,可以用skynet.wait(co)來掛起,skynet.wakeup(co)來喚醒。wakeup只向skynet發了一個信號,需要等skynet框架來調度,而coroutine.resume會直接延續掛起的協程,一般coroutine模塊應用:把coroutine當迭代器來使用