協同程序與線程差不多,也就是一條執行序列,擁有自己獨立的棧,局部變量和指令指針,同時又與其它協同程序共享全局變量和其它大部分東西。線程與協同程序的主要區別在於,一個具有多線程的程序可以同時運行幾個線程,而協同程序卻需要彼此協作地運行。就是說,一個具有多個協同程序的程序在任何時刻只能運行一個協同程序,並且正在運行的協同程序只會在其顯示地掛起時,它的執行才會暫停。
一、 lua協程函數概覽
| 方法 | 描述 |
|---|---|
| coroutine.create() | 創建coroutine,返回coroutine, 參數是一個函數,當和resume配合使用的時候就喚醒函數調用 |
| coroutine.resume() | 重啟coroutine,和create配合使用 |
| coroutine.yield() | 掛起coroutine,將coroutine設置為掛起狀態,這個和resume配合使用能有很多有用的效果 |
| coroutine.status() | 查看coroutine的狀態.注:coroutine的狀態有四種:dead,suspend,running,normal |
| coroutine.wrap() | 創建coroutine,返回一個函數,一旦你調用這個函數,就進入coroutine,和create功能重復 |
| coroutine.running() | 返回正在跑的coroutine,一個coroutine就是一個線程,當使用running的時候,就是返回一個corouting的線程號 |
二、 函數詳解
1. coroutine.create(f)
創建一個主體函數為 f 的新協程。 f 必須是一個 Lua 的函數。 返回這個新協程,它是一個類型為 "thread" 的對象。不會啟動該協程。
local co = coroutine.create(
function()
print("in coroutine")
return "coroutine return"
end)
print(co)
print(coroutine.resume(co))
Output:
thread: 0039B808
in coroutine
true coroutine return
2. coroutine.resume(co, [, val1, ...])
開始或繼續協程co的運行。當第一次執行一個協程時,他會從主函數處開始運行。val1,...這些值會以參數形式傳入主體函數。 如果該協程被掛起,resume 會重新啟動它; val1, ... 這些參數會作為掛起點的返回值。如果協程運行起來沒有錯誤, resume 返回 true 加上傳給 yield 的所有值 (當協程掛起), 或是主體函數的所有返回值(當協程中止)。
local co = coroutine.create(
function (input)
print("input : "..input)
local param1, param2 = coroutine.yield("yield")
print("param1 is : "..param1)
print("param2 is : "..param2)
-- return 也會將結果返回給 resume
return "return"
end)
--第一次執行,將參數傳給input
print(coroutine.resume(co, "function input"))
print("this is main chunk")
--第二次執行,將參數作為yield的返回值,傳給param1 param2
print(coroutine.resume(co, "param1", "param2"))
Output:
input : function input
true yield
this is main chunk
param1 is : param1
param2 is : param2
true return
coroutine.resume 是在保護模式中運行,如果有任何錯誤發生, Lua 是不會顯示任何錯誤, 而是 返回 false 加錯誤消息。同時,這個協程的狀態會變成dead。
local co = coroutine.create(
function()
print("error test")
--調用一個空值得屬性
coroutine.yield(a.a)
end)
-- 返回結果為 false 以及錯誤信息
print(coroutine.resume(co))
-- 協程的狀態變為 dead
print(coroutine.status(co))
Output:
error test
false K:\lua\coroutine.lua:49: attempt to index global 'a' (a nil value)
dead
3. coroutine.yield(...)
掛起正在調用的協程的執行。 傳遞給 yield 的參數都會轉為 resume 的額外返回值。
local co = coroutine.create(
function ()
coroutine.yield("yield1" ,"yield2")
end)
print(coroutine.resume(co))
Output:
true yield1 yield2
4. coroutine.status(co)
以字符串形式返回協程 co 的狀態:
- 當協程正在運行(它就是調用 status 的那個) ,返回 "running";
- 如果協程調用 yield 掛起或是還沒有開始運行,返回 "suspended";
- 如果協程是活動的,都並不在運行(即它正在延續其它協程),返回 "normal";
- 如果協程運行完主體函數或因錯誤停止,返回 "dead"。
local co
local co2 = coroutine.create(function() print("3."..coroutine.status(co)) end)
co = coroutine.create(
function ()
print("2."..coroutine.status(co))
coroutine.resume(co2)
coroutine.yield()
end)
print("1."..coroutine.status(co))
coroutine.resume(co)
print("4."..coroutine.status(co))
coroutine.resume(co)
print("5."..coroutine.status(co))
Output:
1.suspended
2.running
3.normal
4.suspended
5.dead
5. coroutine.running()
返回當前的協程,如果實在主線程,則返回nil
local co = coroutine.create(
function ()
print(coroutine.running())
end)
print(coroutine.running())
coroutine.resume(co)
print(co)
Output:
nil
thread: 003FC9A0
thread: 003FC9A0
** Lua 5.3 有變動 **
6. coroutine.wrap(f)
創建一個主體函數為 f 的新協程。 f 必須是一個 Lua 的函數。 返回一個函數, 每次調用該函數都會延續該協程。 傳給這個函數的參數都會作為 resume 的額外參數。 和 resume 返回相同的值, 只是沒有第一個布爾量。
local wrap = coroutine.wrap(
function (input)
print("input : "..input)
local param1, param2 = coroutine.yield("yield")
print("param1 is : "..param1)
print("param2 is : "..param2)
-- return 也會將結果返回給 resume
return "return"
end)
--第一次執行,將參數傳給input
print(wrap("function input"))
print("this is main chunk")
--第二次執行,將參數作為yield的返回值,傳給param1 param2
print(wrap("param1", "param2"))
Output:
input : function input
yield
this is main chunk
param1 is : param1
param2 is : param2
return
coroutine.wrap不是保護模式運行,如果發生任何錯誤,拋出這個錯誤。
local wrap = coroutine.wrap(
function()
print("error test")
--調用一個空值得屬性
coroutine.yield(a.a)
end)
-- 返回結果為 false 以及錯誤信息
print(coroutine.resume(co))
這段代碼在運行時,會拋出異常。
三、實例
摘自 《Lua 程序設計(第二版)》
-- 使用協程實現生產者消費者
function receive()
local status, value = coroutine.resume(producer)
return value
end
function send(x)
coroutine.yield(x)
end
producer = coroutine.create(
function()
while true do
local x = io.read()
send(x)
end
end)
function consumer ()
while true do
local x = receive()
io.write(x, "\n")
end
end
consumer();
