类似于线程,协程拥有自己独立的栈、局部变量和指令指针。多个协程共享全局变量和其它大部分东西。线程与协程的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协程却需要彼此协作的运行。就是说,一个具有多个协程的程序在任意时刻只能运行一个协程,并且正在运行的协程只会在其显式地要求挂起时,它的执行才会暂停。
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则将内部的状态(数据)返回到主程中。