【Lua】初探協程

類似於線程,協程擁有自己獨立的棧、局部變量和指令指針。多個協程共享全局變量和其它大部分東西。線程與協程的主要區別在於,一個具有多個線程的程序可以同時運行幾個線程,而協程卻需要彼此協作的運行。就是說,一個具有多個協程的程序在任意時刻只能運行一個協程,並且正在運行的協程只會在其顯式地要求掛起時,它的執行纔會暫停。


Lua將所有關於協程的函數放置在一個名爲“coroutine”的table中。函數create用於創建新的協程,它只有一個參數,就是一個函數。該函數的代碼就是協程需要執行的內容。create會返回一個thread類型的值,用以表示新的協程,create的參數常用匿名函數,如以下代碼:

local co = coroutine.create(function () print("Hello World") end)

Lua協程常用的函數有:

方法 描述
coroutine.create() 創建coroutine,返回coroutine, 參數是一個函數,當和resume配合使用的時候就喚醒函數調用
coroutine.resume() 啓動coroutine,和create配合使用
coroutine.yield() 掛起正在調用的協程的執行。 傳遞給 yield 的參數都會轉爲 resume 的額外返回值。
coroutine.status() 查看coroutine的狀態              注:coroutine的狀態有四種:dead,suspend,running, normal
coroutine.wrap() 創建coroutine,返回一個函數,一旦你調用這個函數,就進入coroutine
coroutine.running() 返回正在跑的coroutine,一個coroutine就是一個線程,當使用running的時候,就是返回一個coroutine的線程號。此外,還返回一個布爾值,若當前協程爲主線程,則爲真,反之就爲假

注意 wrap() 與 create() 函數的區別:wrap()返回的是函數,create()返回的是thread變量。所以wrap()的返回值是不能作爲 status() 的參數的,status()函數的入參得是一個 thread變量。


下面通過例子說明各個函數用法:

co1 = coroutine.create(
	function(i)
		print(i)
		print("co1 status: ", coroutine.status(co1))  
       	print(coroutine.running()) 
	end
)
print("co1 status: ", coroutine.status(co1))  
coroutine.resume(co1, 1)
print("co1 status: ", coroutine.status(co1))  

co2 = coroutine.wrap(
	function(i)
		print(i)
        print(coroutine.running()) 
	end
)
co2(2)

co3 = coroutine.create(
    function(j)
		print(j)
        for i=1,10 do
            print(i)
            if i == 3 then
				print("co3 status: ", coroutine.status(co3))  
                print(coroutine.running()) 
            end
            coroutine.yield()
        end
    end
)
 
coroutine.resume(co3, 3) 
coroutine.resume(co3) 
coroutine.resume(co3) 
 
print(coroutine.status(co3))   
print(coroutine.running())
 



輸出如下:

co1 status: 	suspended
1
co1 status: 	running
thread: 0x1206688	false
co1 status: 	dead
2
thread: 0x12053f8	false
3
1
2
3
co3 status: 	running
thread: 0x1205978	false
suspended
thread: 0x11fe018	true

當create一個coroutine的時候就是註冊了一個新事件。當使用resume觸發事件的時候,create的coroutine函數就被執行了,當遇到yield的時候就代表掛起當前線程,等候再次resume觸發事件。



下面一個更細緻的例子:

function foo (a)
    print("foo 函數輸出", a)
    return coroutine.yield(2 * a) -- yield()的參數將會作爲本次resume的返回值,而下次resume的入參將會成爲本次yield的返回值。
end
 
co = coroutine.create(function (a , b)
    print("第一次協程執行輸入", a, b) -- 1 10
    local r = foo(a + 1)
     
    print("第二次協程執行輸入", r)
    local r, s = coroutine.yield(a + b, a - b)  
     
    print("第三次協程執行輸入", r, s)
    return b, "結束協程"                   
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割線----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割線---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 結束協程
print("---分割線---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割線---")
 

輸出結果爲:

第一次協程執行輸入	1	10
foo 函數輸出	2
main	true	4
--分割線----
r
第二次協程執行輸入	r
main	true	11	-9
---分割線---
第三次協程執行輸入	x	y
main	true	10	結束協程
---分割線---
main	false	cannot resume dead coroutine
---分割線---


來一個生產消費者的例子練手:

local producer = coroutine.create(
	function (num)
		local Num = 0
		repeat
			Num = Num + 1
			if num < 5 then
				num = num + 1
				print("producing...", num)
			else
				num = coroutine.yield(num)
				num = num + 1
				print("producing...", num)
			end
		until (Num >= 10)
		coroutine.yield(num)
	end
)

local consumer = coroutine.create(
	function (num)
		while true do
			if num > 0 then
				num = num - 1
				print("consuming...", num)
			else
				print("nothing to be consumed")
				num = coroutine.yield(num)
				print(num)
			end
		end
	end
)

function myFun()
	local num = 0
	_, num = coroutine.resume(consumer, num)
	_, num = coroutine.resume(producer, num)
	_, num = coroutine.resume(consumer, num)
	_, num = coroutine.resume(producer, num)
	_, num = coroutine.resume(consumer, num)
end

myFun()


輸出如下:

nothing to be consumed
producing...	1
producing...	2
producing...	3
producing...	4
producing...	5
5
consuming...	4
consuming...	3
consuming...	2
consuming...	1
consuming...	0
nothing to be consumed
producing...	1
producing...	2
producing...	3
producing...	4
producing...	5
5
consuming...	4
consuming...	3
consuming...	2
consuming...	1
consuming...	0
nothing to be consumed


示例中一共生產了10次。每生產5個,就去消費。消費完了再生產。


resume和yield的配合強大之處在於,resume處於主程中(實際運用中resume不限於主程中),它將外部狀態(數據)傳入到協同程序內部;而yield則將內部的狀態(數據)返回到主程中。


發佈了129 篇原創文章 · 獲贊 47 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章