resume 和 yeild 的协作是 Lua 协程的核心。我们使用一幅图描述一下,有一个大体的印象。对照下面的 coroutine 库的详细解释和最后的代码,应该可以搞清楚协程的概念了。
注:这是在非首次 resume 协程的情况下,resume 和 yield 的互相调用的情况。如果是首次 resume 协程,那么 resume 的参数会直接传递给协程函数。
coroutine.create (f):传一个函数参数,用来创建协程。返回一个 “thread” 对象。
coroutine.isyieldable():如果正在运行的协程可以让出,则返回真。值得注意的是,只有主协程(线程)和 C 函数中是无法让出的。
coroutine.resume(co [, val1, ···]):这是一个非常重要的函数。用来启动或再次启动一个协程,使其由挂起状态变成运行状态。
可以这么说,resume 函数相当于在执行协程中的方法。参数 Val1… 是执行协程 co 时传递给协程的方法。首次执行协程 co 时,参数 Val1… 会传递给协程 co 的函数。
再次执行协程 co 时,参数 Val1… 会作为给协程 co 中上一次 yeild 的返回值。resume 函数返回什么呢?有 3 种情况:
可以看到 resume 无论如何都不会导致程序崩溃。它是在保护模式下执行的。
coroutine.running():用来判断当前执行的协程是不是主线程,如果是,就返回 true。
coroutine.status(co):返回一个字符串,表示协程的状态。有 4 种状态:
状态 | 描述 |
---|---|
running | 如果在协程的函数中调用 status,传入协程自身的句柄,那么执行到这里的时候才会返回 running 状态。 |
suspended | 如果协程还未结束,即自身调用了 yeild 或还没开始运行,那么就是 suspended 状态。 |
normal | 如果协程 A resume 协程 B 时,协程 A 处于的状态为 normal。在协程 B 的执行过程中,协程A 就一直处于 normal 状态。因为它这时候既不是挂起状态、也不是运行状态。 |
dead | 如果一个协程发生错误结束,或正常终止。那么就处于 dead 状态。如果这时候对它调用 resume,将返回 false 和错误消息。 |
coroutine.wrap(f):wrap() 也是用来创建协程的。只不过这个协程的句柄是隐藏的。跟 create() 的区别在于:
coroutine.yield (···):使正在执行的函数挂起。
传递给 yeild 的参数会作为 resume 的额外返回值。同时,如果对该协程不是第一次执行 resume,resume 函数传入的参数将会作为 yield 的返回值。
简单使用 resume yield 函数
#!/usr/bin/lua
print("haicoder(www.haicoder.net)\n")
coco = coroutine.create(function (a,b)
print("resume args:"..a..","..b)
yreturn = coroutine.yield()
print ("yreturn :"..yreturn)
end)
coroutine.resume(coco, 0, 1)
coroutine.resume(coco, 21)
程序运行后,控制台输出如下:
yield 使正在执行的函数挂起。
简单使用 wrap 函数
#!/usr/bin/lua
print("haicoder(www.haicoder.net)\n")
coco2 = coroutine.wrap(function (a,b)
print("resume args:"..a..","..b)
yreturn = coroutine.yield()
print ("yreturn :"..yreturn)
end)
print(type(coco2))
coco2(0,1)
coco2(21)
程序运行后,控制台输出如下:
wrap 用来创建隐藏协程。
模拟生产者消费者问题
#!/usr/bin/lua
print("haicoder(www.haicoder.net)\n")
local newProductor
function productor()
local i = 0
while true do
i = i + 1
send(i) -- 将生产的物品发送给消费者
end
end
function consumer()
while true do
local i = receive() -- 从生产者那里得到物品
print(i)
end
end
function receive()
local status, value = coroutine.resume(newProductor)
return value
end
function send(x)
coroutine.yield(x) -- x表示需要发送的值,值返回以后,就挂起该协同程序
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()
程序运行后,控制台输出如下:
我们使用了协程模拟了生产者消费者问题。