概述
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 协程恢复执行
Lua协程关键技术
--- 创建协程
local producer_co = coroutine.create(function() ... end)
--- 挂起协程(让出执行权)
coroutine.yield(data)
--- 恢复协程执行
coroutine.resume(producer_co)
1 ) 特点:
- 非抢占式协作调度,避免锁竞争
- 状态保存:yield 暂停时保留局部变量状态
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)
end
end
--- 消费者协程
function consumer()
for i = 1, 10 do
while #buffer == 0 do
coroutine.yield() -- 等待生产者生产
end
local item = table.remove(buffer, 1)
print("消费者消费: " .. item)
end
end
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
完整代码实现
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()
运行效果与关键机制
[Producer] Generated: Product-1
[Consumer] Processed: Product-1
[Producer] Generated: Product-2
[Consumer] Processed: Product-2
...
动态平衡原理:
- 当缓冲区满时,push() 中的 yield(“FULL”) 挂起生产者
- 当消费者取走数据后,主循环通过 resume() 唤醒生产者继续工作
工程示例
1 ) 方案1
包含调度器的完整实现
--- 完整生产者-消费者示例
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 i, 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 i, 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
coroutine.yield() -- 防止无限循环
end
end
--- 启动调度器
scheduler()
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
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()
local producer_co = coroutine.create(producer)
local consumer_co = coroutine.create(consumer)
while true do
--- 恢复生产者
local producerstatus = coroutine.resume(producerco)
--- 恢复消费者
local consumerstatus = coroutine.resume(consumerco)
--- 检查是否结束
if not producer_status and #buffer == 0 then
break
end
end
end
--- 启动调度器
scheduler()
3 ) 方案3
增强版本实现(支持多生产者多消费者)
--- 增强版生产者-消费者模型
local Buffer = {}
Buffer.index = Buffer
function Buffer:new(size)
local obj = {
data = {},
size = size or 10,
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)
while self:isFull() do
coroutine.yield("buffer_full")
end
table.insert(self.data, item)
print(string.format("生产者%d生产: %d", coroutine.running(), item))
end
function Buffer:consume()
while self:isEmpty() do
coroutine.yield("buffer_empty")
end
local item = table.remove(self.data, 1)
print(string.format("消费者%d消费: %d", coroutine.running(), item))
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)
end
end)
end
--- 消费者函数
function createConsumer(buffer, id)
return coroutine.create(function()
while true do
buffer:consume()
end
end)
end
--- 增强版调度器
function enhancedScheduler()
local buffer = Buffer:new(5)
local coroutines = {}
--- 创建2个生产者
table.insert(coroutines, createProducer(buffer, 1, 5))
table.insert(coroutines, createProducer(buffer, 2, 5))
--- 创建2个消费者
table.insert(coroutines, createConsumer(buffer, 1))
table.insert(coroutines, createConsumer(buffer, 2))
local running = true
while running do
running = false
for i, co in ipairs(coroutines) do
local success, result = coroutine.resume(co)
if success then
running = true
end
end
end
end
--- 启动增强版调度器
enhancedScheduler()
进阶优化与最佳实践
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
2 ) 性能优化技巧
- 批量处理:支持生产者批量生产数据,减少协程切换次数
- 动态调整:根据缓冲区状态动态调整生产/消费速率
- 超时机制:为等待操作添加超时,避免无限等待
3 ) 实际应用场景
- 游戏开发:角色AI行为控制,每个角色作为独立协程[17]
- 数据处理管道:分阶段处理数据流
- 异步任务管理:管理异步操作的执行顺序
扩展应用场景
-
网络请求批处理
生产者:接收HTTP请求并存入队列
消费者:批量处理请求减少I/O开销 -
游戏逻辑更新
生产者:收集玩家操作事件
消费者:统一计算游戏状态[29] -
日志系统
生产者:多个服务生成日志
消费者:异步写入磁盘文件[5]
结论
通过Lua协程实现生产者-消费者模型具有以下优势:
- 轻量级:协程比线程更轻量,开销小
- 易控制:协作式调度,逻辑清晰
- 解耦合:生产者和消费者无需直接交互,降低系统复杂度
505

被折叠的 条评论
为什么被折叠?



