问题描述
我使用协程实现生产者-消费者模型。我的代码如下:
function send(prod,x)
coroutine.resume(prod,x)
end
function receive()
local x = coroutine.yield()
return x
end
function consumer()
return coroutine.create(function ()
while true do
local x = receive()
io.write(x,"\n")
end
end)
end
function producer(prod)
while true do
local x = io.read()
send(prod,x)
end
end
producer(consumer())
运行此代码时,我得到:
第一条输入消息(“ Hello World”)消失了。它应该打印两次,但现在只能打印一次。我认为,生产者-消费者模式的流程图应如下所示:
我弄错了吗?
解决方法
输入第一个字符串后,将按以下顺序调用函数:
-
send
, -
consumer
返回的协程, -
receive
, -
producer
。 然后程序等待用户输入。
local x = coroutine.yield()
,因此local x = receive()
产生producer
。目前尚未达到io.write(x,"\n")
。
用户输入第二行后,其内容如下:
-
send
,
在 -
local x = receive()
,并输出第二个输入, - 在无休止的循环中,
consumer
调用receive
, -
receive
产生producer
, - 等待用户输入。
consumer
之后恢复以下是更正确的代码:
local function send (x)
coroutine.yield(x)
end
local function receive (prod)
local status,value = coroutine.resume(prod)
return value
end
local function producer()
return coroutine.create(
function ()
while true do
local x = io.read() -- produce new value
send(x)
end
end
)
end
function consumer (prod)
while true do
local x = receive(prod) -- get new value
io.write(x,"\n") -- consume new value
end
end
consumer(producer())
请注意,它是consumer(producer())
,并非相反。另请注意,producer
是协程,而不是consumer
。此外,send
产生并恢复receive
。
consumer
开始执行程序,一次又一次地恢复producer
。如果不是这样,例如您的示例,consumer
直到第二次迭代时才准备使用产品。
UPD:以下是“强制送纸”,即由生产者驱动的代码:
local function send (cons,x)
coroutine.resume (cons,x)
end
local function receive ()
return coroutine.yield()
end
local function consumer()
return coroutine.create (
function (x)
while true do
io.write(x,'\n')
-- corotine.yield() returns the extra arguments of coroutine.resume()
x = receive()
end
end
)
end
function producer (cons)
while true do
local x = io.read() -- make a new value
send (cons,x) -- feed the new value
end
end
producer (consumer ())
与作者的示例不同的是,producer
send
到consumer
,而receive
在write
之后。
进一步阅读:https://www.lua.org/pil/9.2.html。
,第一次“恢复”协程时,它不会直接跳转到第一个yield
,而是使用给定的参数调用包装的函数:
local co = coroutine.wrap(function(aaa)
print(aaa) -- prints "first"
print(coroutine.yield()) -- prints "second"
end)
co("first")
co("second")
在代码中解决此问题的简便方法:
local send,receive =
coroutine.resume,coroutine.yield
function consumer()
return coroutine.create(function(x)
while true do
io.write(x,"\n")
x = receive()
end
end)
end
function producer(consumer)
while true do
local x = io.read()
send(consumer,x)
end
end
producer(consumer())