【Lua】Coroutine 協程和閉包學習

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 -1020 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當迭代器來使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章