類似於線程,協程擁有自己獨立的棧、局部變量和指令指針。多個協程共享全局變量和其它大部分東西。線程與協程的主要區別在於,一個具有多個線程的程序可以同時運行幾個線程,而協程卻需要彼此協作的運行。就是說,一個具有多個協程的程序在任意時刻只能運行一個協程,並且正在運行的協程只會在其顯式地要求掛起時,它的執行纔會暫停。
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
resume和yield的配合強大之處在於,resume處於主程中(實際運用中resume不限於主程中),它將外部狀態(數據)傳入到協同程序內部;而yield則將內部的狀態(數據)返回到主程中。