为什么我的由lua coroutine实现的生产者-消费者模型无法正常工作?

问题描述

我使用协程实现生产者-消费者模型。我的代码如下:

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())

运行此代码时,我得到:

enter image description here

第一条输入消息(“ Hello World”)消失了。它应该打印两次,但现在只能打印一次。我认为,生产者-消费者模式的流程图应如下所示:

enter image description here

我弄错了吗?

解决方法

输入第一个字符串后,将按以下顺序调用函数:

  1. send
  2. consumer返回的协程,
  3. receive
  4. producer。 然后程序等待用户输入。

local x = coroutine.yield(),因此local x = receive()产生producer。目前尚未达到io.write(x,"\n")

用户输入第二行后,其内容如下:

  1. send
  2. consumer之后恢复
  3. local x = receive(),并输出第二个输入,
  4. 在无休止的循环中,consumer调用receive
  5. receive产生producer
  6. 等待用户输入。

以下是更正确的代码:

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 sendconsumer,而receivewrite之后。

进一步阅读: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())