Lua: 基于协程的生产者-消费者模型实现

Lua小技能:http工具方法、使用Lua的协同程序实现生产者-消费者 init("0", 0); require "TSLib" while (true) do if deviceIsLock() == 1 then --判断设备是否锁定 unlockDevice()--解锁无密码的设备 end end 阅读详情

概述


Lua通过协程可以优雅地实现生产者-消费者模型,利用协程的协作式并发特性来完成数据生产与消费的解耦

生产者-消费者模型是一种经典的并发设计模式,通过一个缓冲区(交易场所)来解耦生产者和消费者之间的强依赖关系

在Lua中,协程(coroutine)为实现这种模式提供了天然的支持,因为协程可以在执行过程中暂停并在之后继续执行,非常适合模拟生产与消费的协作过程

模型核心概念


1 ) 三大角色

  • 生产者:生成数据的协程(如数据采集模块)
  • 消费者:处理数据的协程(如数据分析模块)
  • 缓冲区:共享的阻塞队列(解耦生产与消费速度差异)

2 ) 生产者-消费者模型遵循3-2-1原则:

  • 3种关系:生产者-生产者(互斥)、消费者-消费者(互斥)、生产者-消费者(同步与互斥)
  • 2种角色:生产者(数据生成方)、消费者(数据处理方)
  • 1个交易场所:缓冲区(解耦媒介)

在Lua中,生产者负责生成数据并放入缓冲区,消费者从缓冲区取出数据进行处理,两者通过协程协作实现异步处理

2 ) 两种关系

  • 同步:缓冲区空时消费者等待生产,满时生产者等待消费
  • 互斥:同一时刻仅一个协程操作缓冲区

Lua协程的核心API包括

  • coroutine.create(func) - 创建协程
  • coroutine.resume(co, ...) - 恢复协程执行
  • coroutine.yield(...) - 挂起协程并返回控制权
--- 协程基础示例
function coFunc()
    print("协程开始执行")
    coroutine.yield("中断执行")
    print("协程恢复执行")
end
 
co = coroutine.create(coFunc)
print(coroutine.resume(co)) -- 输出: true 中断执行
print(coroutine.resume(co)) -- 输出: true 协程恢复执行

--[[
协程开始执行
true    中断执行
协程恢复执行
true
]]

Lua协程关键技术

基本语法

--- 创建协程 
local producer_co = coroutine.create(function() ... end)
--- 挂起协程(让出执行权)
coroutine.yield(data)
--- 恢复协程执行 
coroutine.resume(producer_co)

示例

--- 创建协程 
local producer_co = coroutine.create(function()
    -- 这里是协程的实际代码 
    print("协程开始执行")
    local data = "生产的数据"
    coroutine.yield(data) -- 挂起协程并返回数据 
    print("协程恢复执行")
end)
 
--- 恢复协程执行 
local success, result = coroutine.resume(producer_co)
if success then 
    print("协程返回的数据:", result)
end 

--[[
协程开始执行
协程返回的数据:    生产的数据
]]

协程相关函数总结

函数功能特点
coroutine.create(f)创建协程返回thread对象,初始状态为suspended
coroutine.resume(co, ...)启动/恢复协程在保护模式下执行,返回执行状态和结果
coroutine.yield(...)挂起协程将参数传递给resume
coroutine.status(co)查看状态返回suspended/running/dead/normal
coroutine.wrap(f)创建包装协程返回函数而非thread对象,错误会直接抛出

1 ) 特点:

  • 非抢占式协作调度,避免锁竞争
  • 状态保存:yield 暂停时保留局部变量状态

2 ) 基础实现

使用协程实现简单的生产者-消费者

-- 创建缓冲区 
local buffer = {}
local buffer_size = 3
 
-- 生产者协程
function producer()
    for i = 1, 10 do
        -- 缓冲区满时挂起
        while #buffer >= buffer_size do 
            print("缓冲区满,生产者等待...")
            coroutine.yield() 
        end 
        table.insert(buffer, i)
        print("[生产] 产品: " .. i .. "\t缓冲区: [" .. table.concat(buffer, ",") .. "]")
        coroutine.yield()  -- 关键:生产后主动让出执行权
    end 
    print("生产者完成所有任务")
end
 
-- 消费者协程  
function consumer()
    for i = 1, 10 do 
        -- 缓冲区空时挂起
        while #buffer == 0 do 
            print("缓冲区空,消费者等待...")
            coroutine.yield()
        end 
        local item = table.remove(buffer, 1)
        print("[消费] 产品: " .. item .. "\t缓冲区: [" .. table.concat(buffer, ",") .. "]")
        coroutine.yield()  -- 关键:消费后主动让出执行权
    end 
    print("消费者完成所有任务")
end 
 
-- 创建协程对象
local co_producer = coroutine.create(producer)
local co_consumer = coroutine.create(consumer)
 
-- 协程调度器 (核心修复)
local active = true 
while active do
    -- 交替唤醒生产者和消费者 
    local r1 = coroutine.resume(co_producer)
    local r2 = coroutine.resume(co_consumer)
    
    -- 检查协程状态 
    local s1 = coroutine.status(co_producer)
    local s2 = coroutine.status(co_consumer)
    
    -- 当两者都完成时退出循环
    if s1 == "dead" and s2 == "dead" then 
        active = false
        print("所有任务完成,最终缓冲区: [" .. table.concat(buffer, ",") .. "]")
    end 
end

--[[
[生产] 产品: 1  缓冲区: [1]
[消费] 产品: 1  缓冲区: []
[生产] 产品: 2  缓冲区: [2]
[消费] 产品: 2  缓冲区: []
[生产] 产品: 3  缓冲区: [3]
[消费] 产品: 3  缓冲区: []
[生产] 产品: 4  缓冲区: [4]
[消费] 产品: 4  缓冲区: []
[生产] 产品: 5  缓冲区: [5]
[消费] 产品: 5  缓冲区: []
[生产] 产品: 6  缓冲区: [6]
[消费] 产品: 6  缓冲区: []
[生产] 产品: 7  缓冲区: [7]
[消费] 产品: 7  缓冲区: []
[生产] 产品: 8  缓冲区: [8]
[消费] 产品: 8  缓冲区: []
[生产] 产品: 9  缓冲区: [9]
[消费] 产品: 9  缓冲区: []
[生产] 产品: 10 缓冲区: [10]
[消费] 产品: 10 缓冲区: []
生产者完成所有任务
消费者完成所有任务
所有任务完成,最终缓冲区: []
]] 

3 ) 缓冲区控制

实现有界缓冲区控制

--- 缓冲区管理 
local Buffer = {}
Buffer.__index = Buffer 
 
function Buffer:new(size)
    local obj = {
        data = {},
        max_size = size or 10
    }
    setmetatable(obj, Buffer)
    return obj 
end
 
function Buffer:put(item)
    if #self.data < self.max_size then 
        table.insert(self.data, item)
        return true
    end 
    return false 
end
 
function Buffer:get()
    if #self.data > 0 then
        return table.remove(self.data, 1)
    end
    return nil
end 
 
function Buffer:is_full()
    return #self.data >= self.max_size
end
 
function Buffer:is_empty()
    return #self.data == 0
end

-- 创建缓冲区实例(容量=3)
local buf = Buffer:new(3)
 
print("=== 初始状态 ===")
print("缓冲区是否为空:", buf:is_empty())  -- true 
print("缓冲区是否为满:", buf:is_full())   -- false 
print("当前元素数量:", #buf.data)          -- 0
print()
 
-- 添加元素 
print("添加 A:", buf:put("A"))  -- true
print("添加 B:", buf:put("B"))  -- true 
print("添加 C:", buf:put("C"))  -- true
print("添加 D (超过容量):", buf:put("D"))  -- false (添加失败)
print("当前元素数量:", #buf.data)          -- 3 
print("缓冲区是否为满:", buf:is_full())     -- true 
print()
 
-- 取出元素
print("取出元素:", buf:get())  -- A 
print("取出元素:", buf:get())  -- B
print("当前元素数量:", #buf.data)          -- 1 
print("缓冲区是否为空:", buf:is_empty())    -- false
print()
 
-- 清空缓冲区
print("取出最后元素:", buf:get())  -- C
print("尝试空取出:", buf:get())    -- nil 
print("缓冲区是否为空:", buf:is_empty())    -- true


--[[
=== 初始状态 ===
缓冲区是否为空:    true
缓冲区是否为满:    false
当前元素数量: 0

添加 A:   true
添加 B:   true
添加 C:   true
添加 D (超过容量):    false
当前元素数量: 3
缓冲区是否为满:    true

取出元素:   A
取出元素:   B
当前元素数量: 1
缓冲区是否为空:    false

取出最后元素: C
尝试空取出:  nil
缓冲区是否为空:    true
]]

完整代码实现


1 ) 环形缓冲区(BlockingQueue)

local BlockingQueue = {}
function BlockingQueue.new(size)
    return {
        buffer = {},
        capacity = size,
        count = 0,
        head = 1,
        tail = 1
    }
end
--- 生产者调用:缓冲区满时挂起
function BlockingQueue:push(item)
    while self.count >= self.capacity do 
        coroutine.yield("FULL")  -- 挂起生产者 
    end
    self.buffer[self.tail] = item
    self.tail = (self.tail % self.capacity) + 1
    self.count = self.count + 1
end
--- 消费者调用:缓冲区空时挂起 
function BlockingQueue:pop()
    while self.count <= 0 do 
        coroutine.yield("EMPTY")  -- 挂起消费者
    end 
    local item = self.buffer[self.head]
    self.head = (self.head % self.capacity) + 1 
    self.count = self.count - 1 
    return item 
end 

2 ) 生产者协程(每秒生成1个数据)

local function producer(queue)
    local id = 1
    while true do 
        local data = "Product-"..id
        queue:push(data)
        print("[Producer] Generated: "..data)
        id = id + 1
        os.execute("sleep 1")  -- 模拟生产延迟 
    end
end

3 ) 消费者协程(每秒消费2个数据)

local function consumer(queue)
    while true do 
        local data = queue:pop()
        print("[Consumer] Processed: "..data)
        os.execute("sleep 0.5")  -- 模拟处理耗时 
    end
end

4 ) 主调度器(驱动协程协作)

local function main()
    local queue = BlockingQueue.new(5)  -- 容量为5的缓冲区
--- 创建协程 
    local p_co = coroutine.create(producer)
    local c_co = coroutine.create(consumer)
--- 循环恢复协程执行
    while true do 
        coroutine.resume(p_co, queue)
        coroutine.resume(c_co, queue)
    end
end
 
main()

5 ) 完整代码如下

-- 阻塞队列实现(保持不变)
local BlockingQueue = {}
BlockingQueue.__index = BlockingQueue 
 
function BlockingQueue.new(size)
    local q = {
        buffer = {},
        capacity = size,
        count = 0,
        head = 1,
        tail = 1,
        waiting_producers = {},  -- 等待的生产者队列
        waiting_consumers = {}   -- 等待的消费者队列
    }
    setmetatable(q, BlockingQueue)
    return q 
end
 
function BlockingQueue:push(item)
    while self.count >= self.capacity do 
        table.insert(self.waiting_producers, coroutine.running())
        coroutine.yield("FULL")
    end 
    self.buffer[self.tail] = item 
    self.tail = (self.tail % self.capacity) + 1
    self.count = self.count + 1
    if #self.waiting_consumers > 0 then 
        coroutine.resume(table.remove(self.waiting_consumers, 1))
    end 
end
 
function BlockingQueue:pop()
    while self.count <= 0 do
        table.insert(self.waiting_consumers, coroutine.running())
        coroutine.yield("EMPTY")
    end 
    local item = self.buffer[self.head]
    self.buffer[self.head] = nil 
    self.head = (self.head % self.capacity) + 1 
    self.count = self.count - 1 
    if #self.waiting_producers > 0 then 
        coroutine.resume(table.remove(self.waiting_producers, 1))
    end 
    return item 
end
 
-- 修改后的生产者(带退出条件)
local function producer(queue, max_items)
    for id = 1, max_items do 
        local data = "Product-"..id 
        queue:push(data)
        print("[Producer] Generated: "..data)
        os.execute("sleep 1")
    end 
    print("[Producer] Completed production")
end 
 
-- 修改后的消费者(带空值检测)
local function consumer(queue)
    while true do 
        local data = queue:pop()
        if not data then 
            print("[Consumer] Queue empty, exiting")
            break 
        end 
        print("[Consumer] Processed: "..data)
        os.execute("sleep 0.5")
    end 
end 
 
-- 修复后的主函数 
local function main()
    local queue = BlockingQueue.new(5)
    local max_produce = 10  -- 生产上限 
    
    -- 创建协程 
    local p_co = coroutine.create(function()
        producer(queue, max_produce)
    end)
    
    local c_co = coroutine.create(function()
        consumer(queue)
    end)
    
    -- 启动协程 
    coroutine.resume(p_co)
    coroutine.resume(c_co)
    
    -- 等待协程完成 
    while coroutine.status(p_co) ~= "dead" or 
          coroutine.status(c_co) ~= "dead" do 
        os.execute("sleep 0.1")
    end 
    
    print("All coroutines completed")
end 
 
-- 启动程序 
main()

运行效果与关键机制

[Producer] Generated: Product-1
[Producer] Generated: Product-2
[Producer] Generated: Product-3
[Producer] Generated: Product-4
[Producer] Generated: Product-5
[Consumer] Processed: Product-1
[Consumer] Processed: Product-2
[Consumer] Processed: Product-3
[Consumer] Processed: Product-4
[Consumer] Processed: Product-5
All coroutines completed

动态平衡原理:

  • 当缓冲区满时,push() 中的 yield(“FULL”) 挂起生产者
  • 当消费者取走数据后,主循环通过 resume() 唤醒生产者继续工作

工程示例


1 ) 方案1

包含调度器的完整实现

-- 缓冲区类实现
Buffer = {}
Buffer.__index = Buffer
 
function Buffer:new(capacity)
    local obj = {
        data = {},
        head = 1,    -- 读取位置
        tail = 1,    -- 写入位置
        count = 0,   -- 当前元素数量
        capacity = capacity or 5  -- 默认容量5
    }
    setmetatable(obj, Buffer)
    return obj
end
 
function Buffer:is_full()
    return self.count >= self.capacity 
end 
 
function Buffer:is_empty()
    return self.count == 0 
end 
 
function Buffer:put(item)
    if self:is_full() then
        return false
    end 
    self.data[self.tail] = item
    self.tail = (self.tail % self.capacity) + 1  -- 循环缓冲区 
    self.count = self.count + 1 
    return true 
end 
 
function Buffer:get()
    if self:is_empty() then 
        return nil 
    end
    local item = self.data[self.head]
    self.data[self.head] = nil  -- 清除引用 
    self.head = (self.head % self.capacity) + 1  -- 循环缓冲区
    self.count = self.count - 1
    return item
end
 
-- 创建容量为3的缓冲区 
local buffer = Buffer:new(3)
 
-- 生产者协程 
local producer_co = coroutine.create(function()
    for i = 1, 20 do
        while buffer:is_full() do 
            print("缓冲区已满,生产者等待...")
            coroutine.yield()
        end 
        buffer:put(i)
        print("生产者生产: " .. i)
        coroutine.yield() -- 让出控制权
    end 
    print("生产者完成工作")
end)
 
-- 消费者协程
local consumer_co = coroutine.create(function()
    for i = 1, 20 do 
        while buffer:is_empty() do 
            print("缓冲区为空,消费者等待...")
            coroutine.yield()
        end
        local item = buffer:get()
        print("消费者消费: " .. item)
        coroutine.yield() -- 让出控制权 
    end
    print("消费者完成工作")
end)
 
-- 协程调度器 
local function scheduler()
    local producers = {producer_co}  -- 修正:直接存储协程对象 
    local consumers = {consumer_co}  -- 非表嵌套 
    
    while true do 
        local all_done = true 
        
        -- 调度生产者 
        for _, co in ipairs(producers) do 
            if coroutine.status(co) ~= "dead" then
                local success, err = coroutine.resume(co)
                if not success then
                    print("生产者协程错误: " .. tostring(err))
                end 
                all_done = false 
            end 
        end
        
        -- 调度消费者 
        for _, co in ipairs(consumers) do 
            if coroutine.status(co) ~= "dead" then 
                local success, err = coroutine.resume(co)
                if not success then 
                    print("消费者协程错误: " .. tostring(err))
                end
                all_done = false 
            end
        end 
        
        if all_done then
            break 
        end
    end 
end 
 
-- 启动调度器 
scheduler()

--[[
生产者生产: 1
消费者消费: 1
生产者生产: 2
消费者消费: 2
生产者生产: 3
消费者消费: 3
生产者生产: 4
消费者消费: 4
生产者生产: 5
消费者消费: 5
生产者生产: 6
消费者消费: 6
生产者生产: 7
消费者消费: 7
生产者生产: 8
消费者消费: 8
生产者生产: 9
消费者消费: 9
生产者生产: 10
消费者消费: 10
生产者生产: 11
消费者消费: 11
生产者生产: 12
消费者消费: 12
生产者生产: 13
消费者消费: 13
生产者生产: 14
消费者消费: 14
生产者生产: 15
消费者消费: 15
生产者生产: 16
消费者消费: 16
生产者生产: 17
消费者消费: 17
生产者生产: 18
消费者消费: 18
生产者生产: 19
消费者消费: 19
生产者生产: 20
消费者消费: 20
生产者完成工作
消费者完成工作
]]

2 ) 方案2

--- 生产者-消费者模型基础实现 
local buffer = {}
local buffer_size = 3  -- 缓冲区大小 
 
--- 生产者协程 
function producer()
    for i = 1, 10 do 
        --- 等待缓冲区有空位 
        while #buffer >= buffer_size do 
            coroutine.yield()  -- 暂停,等待消费者消费 
        end 
        --- 生产数据 
        table.insert(buffer, i)
        print("生产者生产: " .. i)
        --- 通知消费者 
        coroutine.yield()
    end 
    print("生产者结束")
end 
 
--- 消费者协程 
function consumer()
    while true do 
        --- 等待缓冲区有数据 
        while #buffer == 0 do 
            coroutine.yield()  -- 暂停,等待生产者生产 
        end 
        --- 消费数据 
        local data = table.remove(buffer, 1)
        print("消费者消费: " .. data)
        --- 通知生产者 
        coroutine.yield()
    end 
end 
 
--- 协程调度器 
function scheduler()
    -- 修正1:统一变量命名(原变量名存在下划线不一致问题)
    local producer_co = coroutine.create(producer)
    local consumer_co = coroutine.create(consumer)
    local producer_done = false   -- 新增生产者完成标志 
    
    while true do 
        --- 恢复生产者(仅当未完成时)
        if not producer_done then 
            -- 修正2:修复变量名拼写错误(原为producerco)
            local status, msg = coroutine.resume(producer_co)
            if not status then  -- 协程执行出错 
                print("生产者错误: " .. tostring(msg))
                break
            elseif coroutine.status(producer_co) == "dead" then
                producer_done = true  -- 标记生产者完成 
                print("生产者协程结束")
            end
        end 
        
        --- 恢复消费者 
        -- 修正3:修复变量名拼写错误(原为consumerco)
        local status, msg = coroutine.resume(consumer_co)
        if not status then 
            print("消费者错误: " .. tostring(msg))
            break 
        end
        
        --- 修正4:完善结束条件(生产者完成且缓冲区为空)
        if producer_done and #buffer == 0 then
            print("所有任务完成")
            break
        end 
    end
end 
 
--- 启动调度器 
scheduler()

--[[
生产者生产: 1
消费者消费: 1
生产者生产: 2
消费者消费: 2
生产者生产: 3
消费者消费: 3
生产者生产: 4
消费者消费: 4
生产者生产: 5
消费者消费: 5
生产者生产: 6
消费者消费: 6
生产者生产: 7
消费者消费: 7
生产者生产: 8
消费者消费: 8
生产者生产: 9
消费者消费: 9
生产者生产: 10
消费者消费: 10
生产者结束
生产者协程结束
所有任务完成
]]

3 ) 方案3

增强版本实现(支持多生产者多消费者)

local Buffer = {}
Buffer.__index = Buffer  -- 修复元表设置 
 
function Buffer:new(size)
    local obj = {
        data = {},  -- 直接初始化空数组 
        size = size or 5,
        producers = {},
        consumers = {}
    }
    setmetatable(obj, Buffer)
    return obj 
end
 
function Buffer:isFull()
    return #self.data >= self.size 
end 
 
function Buffer:isEmpty()
    return #self.data == 0 
end 
 
function Buffer:produce(item, id)  -- 添加生产者ID参数
    while self:isFull() do 
        coroutine.yield("buffer_full")
    end 
    table.insert(self.data, item)
    print(string.format("生产者%d生产: %d", id, item))  -- 使用传入的ID
end
 
function Buffer:consume(id)  -- 添加消费者ID参数
    while self:isEmpty() do
        coroutine.yield("buffer_empty")
    end
    local item = table.remove(self.data, 1)
    print(string.format("消费者%d消费: %d", id, item))  -- 使用传入的ID
    return item
end
 
--- 生产者函数 
function createProducer(buffer, id, count)
    return coroutine.create(function()
        for i = 1, count do
            local item = id * 100 + i 
            buffer:produce(item, id)  -- 传递ID
        end 
        print("生产者"..id.."结束")
    end)
end 
 
--- 消费者函数(添加结束条件)
function createConsumer(buffer, id, total_items)
    return coroutine.create(function()
        local consumed = 0
        while consumed < total_items do  -- 限制消费总数 
            buffer:consume(id)  -- 传递ID 
            consumed = consumed + 1 
        end
        print("消费者"..id.."结束")
    end)
end
 
--- 增强版调度器 
function enhancedScheduler()
    local buffer = Buffer:new(5)
    local coroutines = {}
    local total_items = 10  -- 总生产量(2生产者*5)
    
    -- 创建2个生产者
    table.insert(coroutines, createProducer(buffer, 1, 5))
    table.insert(coroutines, createProducer(buffer, 2, 5))
    
    -- 创建2个消费者(传入总消费量)
    table.insert(coroutines, createConsumer(buffer, 1, total_items/2))
    table.insert(coroutines, createConsumer(buffer, 2, total_items/2))
    
    -- 优化调度:记录活跃协程数 
    local active = #coroutines
    while active > 0 do
        active = 0
        for i, co in ipairs(coroutines) do 
            if coroutine.status(co) ~= "dead" then 
                active = active + 1
                local success, msg = coroutine.resume(co)
                -- 可选:添加缓冲区状态判断减少空转 
                -- if msg == "buffer_empty" or msg == "buffer_full" then 
                --    -- 可在此添加休眠逻辑
                -- end
            end 
        end
    end 
end 
 
--- 启动增强版调度器 
enhancedScheduler()

--[[
生产者1生产: 101
生产者1生产: 102
生产者1生产: 103
生产者1生产: 104
生产者1生产: 105
生产者1结束
消费者1消费: 101
消费者1消费: 102
消费者1消费: 103
消费者1消费: 104
消费者1消费: 105
消费者1结束
生产者2生产: 201
生产者2生产: 202
生产者2生产: 203
生产者2生产: 204
生产者2生产: 205
生产者2结束
消费者2消费: 201
消费者2消费: 202
消费者2消费: 203
消费者2消费: 204
消费者2消费: 205
消费者2结束
]]

进阶优化与最佳实践


1 ) 错误处理机制

--- 带错误处理的版本 
function safeProducer(buffer, id, count)
    return coroutine.create(function()
        for i = 1, count do 
            local success, err = pcall(function()
                local item = id * 100 + i 
                buffer:produce(item)
            end)
            
            if not success then 
                print("生产者错误: " .. err)
                break 
            end 
        end 
    end)
end

完整示例

local buffer = {
    max_size = 5,
    queue = {},
    count = 0,
    head = 1,
    tail = 1 
}
 
-- 全局生产者存活计数器 
local activeProducers = 0
 
-- 缓冲区生产方法
function buffer:produce(item)
    if self.count >= self.max_size then 
        return false
    end 
    self.queue[self.tail] = item
    self.tail = (self.tail % self.max_size) + 1
    self.count = self.count + 1
    return true
end
 
-- 缓冲区消费方法
function buffer:consume()
    if self.count <= 0 then 
        return nil 
    end
    local item = self.queue[self.head]
    self.queue[self.head] = nil
    self.head = (self.head % self.max_size) + 1
    self.count = self.count - 1
    return item
end
 
-- 带错误处理的生产者
function safeProducer(buffer, id, count)
    return coroutine.create(function()
        activeProducers = activeProducers + 1  -- 注册生产者
        
        for i = 1, count do
            local success, err = pcall(function()
                -- 模拟错误:生产第3个物品时触发
                if i == 3 then
                    error("生产过程中出现意外错误!")
                end
                
                local item = id * 100 + i
                while not buffer:produce(item) do 
                    print(string.format("生产者 %d: 缓冲区已满,等待中...", id))
                    coroutine.yield()
                end
                print(string.format("生产者 %d: 成功生产 %d", id, item))
            end)
            
            if not success then 
                print("生产者错误: " .. err)
                break
            end 
            coroutine.yield()
        end
        
        activeProducers = activeProducers - 1  -- 注销生产者 
        print(string.format("生产者 %d 退出生产", id))
    end)
end
 
-- 修复的消费者(增加生产者存活检查)
function safeConsumer(buffer, id, count)
    return coroutine.create(function()
        local consumed = 0
        while consumed < count do 
            local success, err = pcall(function()
                -- 模拟错误:消费第5个物品时触发 
                if consumed == 4 then 
                    error("消费过程中出现数据异常!")
                end 
                
                local item = buffer:consume()
                if item then
                    consumed = consumed + 1
                    print(string.format("消费者 %d: 成功消费 %d", id, item))
                else
                    -- 关键修复:当缓冲区空且无存活生产者时主动退出
                    if activeProducers == 0 then
                        error("所有生产者已退出,终止消费等待")
                    end
                    print(string.format("消费者 %d: 缓冲区为空,等待中...", id))
                    coroutine.yield()
                end
            end)
            
            if not success then
                print("消费者错误: " .. err)
                break 
            end
            coroutine.yield()
        end 
        print(string.format("消费者 %d 完成消费任务", id))
    end)
end 
 
-- 主程序
activeProducers = 0  -- 初始化计数器 
local producer1 = safeProducer(buffer, 1, 10)
local consumer1 = safeConsumer(buffer, 1, 8)
local consumer2 = safeConsumer(buffer, 2, 5)
 
-- 协程调度器
local function scheduler()
    while true do 
        local anyActive = false
        
        -- 检查生产者
        if coroutine.status(producer1) == "suspended" then
            anyActive = true
            coroutine.resume(producer1)
        end 
        
        -- 检查消费者 
        if coroutine.status(consumer1) == "suspended" then
            anyActive = true
            coroutine.resume(consumer1)
        end
        
        if coroutine.status(consumer2) == "suspended" then
            anyActive = true
            coroutine.resume(consumer2)
        end
        
        -- 无活跃协程时退出循环 
        if not anyActive then break end
    end 
    print("所有任务完成!")
end
 
-- 启动调度器
scheduler()


--[[
生产者 1: 成功生产 101
消费者 1: 成功消费 101
消费者 2: 缓冲区为空,等待中...
生产者 1: 成功生产 102
消费者 1: 成功消费 102
生产者错误: 912.lua:44: 生产过程中出现意外错误!
生产者 1 退出生产
消费者错误: 912.lua:85: 所有生产者已退出,终止消费等待
消费者 1 完成消费任务
消费者错误: 912.lua:85: 所有生产者已退出,终止消费等待
消费者 2 完成消费任务
所有任务完成!
]]

2 ) 性能优化技巧

  • 批量处理:支持生产者批量生产数据,减少协程切换次数
  • 动态调整:根据缓冲区状态动态调整生产/消费速率
  • 超时机制:为等待操作添加超时,避免无限等待

3 ) 实际应用场景

  • 游戏开发:角色AI行为控制,每个角色作为独立协程[17]
  • 数据处理管道:分阶段处理数据流
  • 异步任务管理:管理异步操作的执行顺序

扩展应用场景


  1. 网络请求批处理
    生产者:接收HTTP请求并存入队列
    消费者:批量处理请求减少I/O开销

  2. 游戏逻辑更新
    生产者:收集玩家操作事件
    消费者:统一计算游戏状态[29]

  3. 日志系统
    生产者:多个服务生成日志
    消费者:异步写入磁盘文件[5]

结论


通过Lua协程实现生产者-消费者模型具有以下优势:

  • 轻量级:协程比线程更轻量,开销小
  • 易控制:协作式调度,逻辑清晰
  • 解耦合:生产者和消费者无需直接交互,降低系统复杂度
Lua协程Coroutine 每当调用这个返回函数,都会执行协程主函数运行。协同程序(coroutine,这里简称协程)是一种类似于线程(thread)的东西,它拥有自己独立的栈、局部变量和指令指针,可以跟其他协程共享全局变量和其他一些数据,并且具有一种挂起(yield)中断协程主函数运行,下一次激活恢复协程会在上一次中断的地方继续执行(resume)协程主函数的控制机制。函数 coroutine.create 用于创建一个新的协程,它只有一个以函数形式传入的参数,该函数是协程的主函数,它的代码是协程所需执行的内容。 阅读详情

相关推荐

【第三只眼:Oracle通过归档日志查看系统的业务高峰sql】

根据oracle归档的业务高峰量,查找业务sql,进行优化

霍然的专栏 580

【Lua基础系列】协程

【Lua基础系列】协程 大家好,我是Lampard~~ 欢迎来到Lua进阶系列的博客 前文再续,书接上一回。今天和大家讲解一下lua中的协程 (一) 什么是协程 Lua 协同程序(coroutine)与线程(这里的线程指的是操作系统的线程)比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。 一个多线程程序可以同时运行几个线程(并发执行、抢占),而协程却需要彼此协作地运行,...

Lampard的博客 6442

做形态学方法的团队_“设计形态学”与“第三自然”

文/邱松(清华大学 美术学院)摘要:“设计形态学”是基于“形态学”研究的设计基础学科。“形态”既是该学科研究的主体,又是与其它学科相连接的“纽带”。实际上,不管是供养我们的地球家园,还是神秘莫测的浩瀚宇宙,均由数量纷繁、千姿百态的“形态”构筑而成。形态通常被分成两大类:一类是源于“第一自然”的“自然形态”;另一类是来自“第二自然”的“人造形态”。而这两类形态正是“设计形态学”研究的两条...

weixin_31548723的博客 2684

Lua : Coroutine(协程)

Lua 协程 基础知识详解

Mr.Sugarcane 1654

ROS2 多机器人导航仿真入门教程(一)

RMF 是建立在 ROS 2 基础上的一系列可重复使用、可扩展的库和工具,可实现任何类型机器人系统的异构机群的互操作性。RMF 利用标准化的通信协议与部署机器人的基础设施、环境和自动化系统进行通信,以优化关键资源(如机器人、电梯、门、通道等)的使用。它通过资源分配和通过 RMF 核心(将在本书后面详细介绍)防止共享资源冲突,从而为系统增加智能。RMF 灵活而强大,几乎可以在任何通信层上运行,并与任何数量的物联网设备集成。RMF 的架构设计允许随着环境自动化水平的提高而扩展。

2506_90492529的博客 1661

利用大模型和聚类算法找出 Excel 文件中重复或相似度高的数据,并使用 FastAPI 进行封装的详细方案

tokenizer将输入的文本列表texts转换为 PyTorch 张量,同时进行填充和截断操作,以确保所有输入具有相同的长度。对输入进行推理,得到模型的输出。对模型的最后一层隐藏状态求平均,得到每个文本的向量表示。.numpy()将 PyTorch 张量转换为 NumPy 数组。使用DBSCAN算法对文本向量进行聚类。eps是邻域半径,是形成核心点所需的最小样本数。fit方法对输入的向量进行聚类,并返回每个样本所属的簇标签。try:定义了一个 POST 请求的 API 路由,路径为。

AI的内部世界的博客 1069

协程编程的奥秘:Lua中协程使用的全面解析与技巧分享

协程基础。 生产者-消费者问题。 协程相关API说明。 将协程用作迭代器。 事件驱动式编程。

Lion_Long的博客 2335

Python高阶爬虫:逆向工程模拟Wind/同花顺等金融终端数据抓取

本文详细阐述了模拟金融数据终端进行数据抓取的全套技术方案。我们从法律合规性讲起,选择了Playwright作为核心自动化工具,通过两个实战案例(同花顺网页版和Wind桌面版)演示了从分析、模拟到数据解析的完整流程,并进一步介绍了构建健壮爬虫系统所需的高级技巧。我们的策略是:启动一个由Playwright控制的浏览器,模拟真人登录,然后监听所有网络请求,从中过滤出我们需要的那个数据API。if key in ['f43', 'f169', 'f170', 'f46', 'f60']: # 价格类字段。

2201_76125261的博客 546

lua协程

Lua 协程(Coroutine)是一种轻量级的线程,也被称为协作式多线程。与操作系统管理的线程不同,协程是由程序自身来控制调度的,属于协作式多任务处理机制。这意味着协程只有在显式地让出执行权时,才会暂停执行,将控制权交给其他协程,而不是由操作系统强制切换。轻量级:创建和销毁协程的开销远小于创建和销毁线程。非抢占式调度:程序员可以精确控制协程的执行流程,避免了线程同步和锁带来的复杂性。Lua 协程通过协作式挂起和恢复机制,提供了轻量级的多任务处理能力。

m0_45371381的博客 1168

设计四翼无人机(Quadcopter)的自主降落控制算法

为四翼无人机建立动力学模型,包括其姿态(Pitch, Roll, Yaw)和位置(X, Y, Z)的变化。可以使用Aerospace Blockset中的预制模块,或者根据具体需求构建自定义模型。示例:简化动力学模型利用块模拟四翼无人机的基本行为,确保模型能够反映电机输出如何影响姿态和位置变化。

amy_mhd的博客 244

[Dify实战] 多轮对话状态管理:上下文保持与槽位填充

本文探讨了基于LLM的对话应用中多轮对话状态管理的核心挑战与解决方案。文章指出,单纯的"能聊"远远不够,关键在于建立有效的状态管理机制,包括上下文保持和槽位填充两大核心能力。通过Dify实战视角,详细阐述了如何设计工程化落地方案:采用混合策略的上下文管理(窗口+摘要+关键槽位)、JSON Schema强约束的槽位抽取、统一状态对象存储、冲突处理机制和优化追问策略。文章特别强调,合理的状态管理能将"聊天"转化为"可执行的业务流程",避免模型成为&qu

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 243

【Lua进阶系列】协程

【Lua进阶系列】协程 大家好,我是Lampard猿奋~~ 欢迎来到Lua进阶系列的博客,今天和大家分享一下lua中关于协程的知识点 (一)什么是协程 Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它几乎一切资源。 一个多线程程序可以同时并行运行几个线程,而协程却需要彼此协作地运行,并非真正的多线程。即一个多协程程...

Lampard的博客 1033

25、格式化字符串攻击全解析

本文深入解析了格式化字符串攻击的原理与利用方式,涵盖缓冲区溢出与格式化字符串漏洞的区别,详细介绍了printf函数的工作机制、常见格式化说明符及其安全隐患。文章分析了三类典型编程错误导致的漏洞,并展示了如何通过恶意格式化字符串实现拒绝服务、内存读取和写入攻击。同时,提供了有效的防御措施,包括输入验证、使用安全函数、避免直接使用用户输入作为格式化字符串以及启用编译器安全选项,帮助开发者防范此类高危漏洞。

beer8的博客 85

Lua 协程 + 过滤器实现生产者和消费者

Lua还在精进中,期待您的关注~~

Cauchy的博客 456

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Lua 协程 coroutine

协程是协同程序的简称,顾名思义,就是协同工作的程序。协程拥有自己独立的桟、局部变量和PC计数器,同时又与其他协同程序共享全局变量和其他大部分东西;    协程与线程的主要区别在于,一个多线程程序可以同时运行几个线程(并发执行、抢占),而协同程序却需要彼此协作地运行,即一个多协程程序在任意时刻只能运行一个协程,并且正在执行的协程只会在其显式地要求挂起(suspend)时,它的执行才会暂停(无抢占

iCode0410的专栏 1683

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

Lua_协程协同_管道与过滤器_迭代器协程_生产者消费者lua_多线程_lua爬虫(15)

Lua_协程协同_管道与过滤器_迭代器协程_生产者消费者lua_多线程_lua爬虫(15)

稳稳C9的博客 1159

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Lua协程 基础和使用汇总

一、协程基础 简单介绍 lua协程和线程类似,拥有独立堆栈,独立局部变量;主要区别在于lua可以同时运行多个线程,但是在同一时刻只能有一个协程在运行,并且这个正在运行的协程只有在明确要求挂起的时候才会被挂起,比如调用yield,或者启动别的协程; coroutine.create(func) 创建一个线程,返回一个线程实例 coroutine.wrap(func) 创建一个协程,但是返回的是一个函数; coroutine.resume(co,...) 启动一个线程,并将参数传给调用的函数

mozhi 2189

Lua--协程

Lua协程编程摘要:协程通过coroutine.create创建,初始为挂起状态。coroutine.status可查看协程状态(挂起、运行、死亡、正常)。通过resume启动/yield挂起实现协作式多任务,yield可保存状态并传递参数。resume-yeild机制实现双向数据交换:yield参数成为resume返回值,resume参数成为yield返回值。该机制可解决生产者-消费者模型中的主循环冲突问题,通过协程封装使消费者主导流程,并支持通过过滤器实现中间数据处理。协程执行错误会通过resume返回

2401_83191546的博客 1337

【lua学习】协程 元表 处理多个生产者与消费者的问题 应用skynet消息传递

【lua学习】协程 元表 处理多个生产者与消费者的问题 之前写了队列queue的类,现在想处理多个生产者与消费者的问题 协程类封装起来, -- coroutines.lua coroutines = {} local function sleep(n) os.execute("sleep "..n) end function coroutines:doProducter(id...

SaySomething 307

LUA学习

当一个协程运行结束时,主函数所返回的值都将变成对应函数resume的返回值。(7)Lua语言提供的是所谓的非对称协程,也就是说需要两个函数来控制协程的执行,一个用于挂起协程的执行,另一个用于恢复执行。函数create 用于创建新协程,该函数只有一个参数,即协程要执行的代码的函数(协程体)函数create返回一个"thread"类型的值,即新协程。Lua中的栈有两排索引,正数1索引的位置在栈底,负数索引-1在栈顶,这样做的好处是不需要知道栈的大小,只需要查找正负索引1的位置就能确定栈顶和栈底的位置。

记录成长的博客 1302

协同进程实现生产者消费者

代码及注释如下,使用print函数打印值和行号,可以看到代码的运行路径 --[[ resume协程,如果协程执行的过程中调用yield函数,则resume函数返回yield的参数 ]] function receive(prod) local status, value = coroutine.resume(prod) print(value, debug.getinfo(1).cur...

Default 518

Lua 程序设计:协程

文章目录协程基础生产者和消费者问题 协程 一个协程(coroutine)与线程(thread,这里指“多线程”中的线程)类似:它是一个执行序列,拥有自己的栈,局部变量,指令指针,但是与其他的协程共享全局变量和其他大部分东西。 从概念上讲,协程与线程最大的区别是:在一个多处理器机器上,一个多线程程序可以并行地执行多个线程。而协程是协作性的,一个程序任意时刻只能执行一个协程,并且只有这个协程在显示地要求挂起时,它的执行才会暂停。 基础 Lua 将所有协程的函数放在一个名为 “coroutine” 的 table

alenliu's blog 1010

【Lua】协程

协程(coroutine)是Lua语言中的一个核心特性,它是一种轻量级的线程,可以在特定的时刻挂起和恢复执行,使得程序的控制流程可以更加灵活地切换和管理。Lua的协程通过coroutine库来实现,通过使用协程,可以将一个复杂的任务分割成多个阶段,并可以在各个阶段之间暂停和恢复执行。协程的概念类似于线程,但是与线程相比,协程更加轻量级,没有操作系统线程的开销和限制。使用协程可以很方便地实现一些需要多个步骤、需要等待的任务,例如网络通信、游戏中的动作处理等。

你好 1133

lua 协程 简单的生产者和消费者

function Sleep(n) if n > 0 then os.execute("ping -n " .. tonumber(n + 1) .. " localhost > NUL") end end product = coroutine.wrap(function(a) while a>0 do local ret = 0 if a >100 then ret = consumer(a) else local num = getRando..

u011747225的专栏 342

Lua协程

目录 1.什么是协程 1.1 进程和线程 1.2 协程 1.2.1 协程挂起与唤醒 1.3 进程、线程和协程的区别 2.Lua协程 协程是追求极限性能和优美的代码结构的产物,协程允许我们写同步代码的逻辑,却做着异步的时,避免了回调嵌套,使得代码逻辑清晰。 1.什么是协程 1.1 进程和线程 在了解什么是协程之前,我们先要了解什么是进程和线程。 进程是应用程序的启动实例,比如我...

Martin的博客 1651

Lua程序设计 | 协程与反射

协程与反射

ZY-JIMMY 1512
上一篇: Lua: 错误处理与调试技术详解之从基础到EmmyLua高级调试
下一篇: Lua: 协程编程详解之从基础到多任务处理与应用实战
Wang's Blog
博客等级 码龄12年 7438粉丝 2422原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值