为什么我的用lua协程实现的生产者消费者模型不能按预期工作?

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

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

我理解错了吗?

点赞
用户6632736
用户6632736

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

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

local x = coroutine.yield(),因此 local x = receive() 会回到 producer,此时还没有执行 io.write(x, "\n")

用户输入第二行后,会按以下方式执行:

  1. send
  2. local x = receive() 后 ,consumer 被恢复并打印第二个输入。
  3. 在其无限循环中,consumer 调用 receive
  4. receive 会回到 producer
  5. 等待用户输入。

以下是更正确的代码:

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()     -- 产生新值
                send(x)
            end
        end
    )
end

function consumer(prod)
    while true do
        local x = receive(prod)   -- 获取新值
        io.write(x, "\n")          -- 消耗新值
    end
end

consumer(producer())

注意,它是 consumer(producer()),而不是另一个方式。还要注意 producer 是协程,而不是 consumersend 会挂起,而 receive 会恢复。

consumer 开始程序,一遍又一遍地恢复 producer。如果像你的例子那样,反过来,直到第二次迭代,consumer 才准备好消费产品。

更新: 以下是“强制饲养”,即生产者驱动的代码:

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')
                -- coroutine.yield() 返回 coroutine.resume()的额外参数
                x = receive()
            end
        end
    )
end

function producer(cons)
    while true do
        local x = io.read() -- 创建新值
        send(cons, x)      -- 提供新值
    end
end

producer(consumer())

与作者的示例的不同之处在于 producer 将值发送到 consumer 上,并且 receivewrite 之后执行。

进一步阅读:https://www.lua.org/pil/9.2.html

2020-10-06 12:29:44
用户4984564
用户4984564

第一次执行“resume”协同程序时,它不会直接跳转到第一个“yield”,而是使用给定的参数调用包装函数:

local co = coroutine.wrap(function(aaa)
   print(aaa) -- 打印 "first"
   print(coroutine.yield()) -- 打印 "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())
2020-10-07 07:58:25