之前写了队列queue的类,现在想处理多个生产者与消费者的问题
协程类封装起来,
-- coroutines.lua
coroutines = {}
local function sleep(n)
os.execute("sleep "..n)
end
function coroutines:doProducter(id)
while true do
print("[coroutine] producter", id, "running sleep ", PRODUCTER_SLEEP)
sleep(PRODUCTER_SLEEP) -- 生产者协程生产需要a秒
local index, res = coroutine.yield()
id = index
if res == 1 then
break
end
end
print("[coroutine] Producter", id,"dead")
end
function coroutines:doConsumer(id)
while true do
print("[coroutine] consumer", id, "running sleep ", CONSUMER_SLEEP)
sleep(CONSUMER_SLEEP)-- 消费者协程消费需要b秒
local index, res = coroutine.yield()
id = index
if res == 1 then
break
end
end
print("[coroutine] Consumer", id, "dead")
end
-- flag: 0 生产者、1 消费者
function coroutines:new(id, flag)
print("[coroutine] new: id", id, "flag", flag)
local tempCo
if flag == 0 then
tempCo = coroutine.create(self.doProducter, id)
else
tempCo = coroutine.create(self.doConsumer, id)
end
local tempObj = {
_id = id or 0,
_type = flag or 0,
_co = tempCo or nil,
}
setmetatable(tempObj, self)
self.__index = self
return tempObj
end
return coroutines
-- main.lua
local coroutines = require ("coroutines")
local queue = require("queue")
local PRODUCTERS = 3
local CONSUMERS = 1
local PRODUCTER_SLEEP = 3
local CONSUMER_SLEEP = 1
local function init(tempProducter, tempConsumer)
print("[main] init")
for i = 0, PRODUCTERS - 1 do
tempProducter[i] = coroutines:new(i, 0)
coroutine.resume(tempProducter[i]._co, tempProducter[i]._id)
end
for i = 0, CONSUMERS - 1 do
tempConsumer[i] = coroutines:new(i, 1)
coroutine.resume(tempConsumer[i]._co, tempConsumer[i]._id)
end
end
--[[
-- 0 表示生产 1 表示消费
-- 在生产者正在工作的队列里面找当前生产者是否空闲
--]]
local function isFlag(flag)
if flag == 0 then
if tempQueue:isFull() then
print("[main] Producter Error: Queue Full\n")
return false
else
for i = 0, PRODUCTERS - 1 do
if not tempQueue:find(i) then
productOrConsume(i, flag)
break
end
end
print("\n")
return true
end
else
if tempQueue:isEmpty() then
print("[main] Consumer Error: Queue Empty\n")
return false
else
for i = 0, CONSUMERS - 1 do
productOrConsume(i, flag)--只有一个不作判断
end
print("\n")
return true
end
end
end
function productOrConsume(id, flag)
if flag == 0 then
coroutine.resume(tempProducter[id]._co, tempProducter[id]._id, 0)-- 0表运行 1表示结束
tempQueue:push(tempProducter[id]._id) --生产者协程id
print("[main] Producter sussess, queue size:", tempQueue:getSize())
else
coroutine.resume(tempConsumer[id]._co, tempConsumer[id]._id, 0)
tempQueue:pop() -- 先入先出
print("[main] Consumer sussess, queue size:", tempQueue:getSize())
end
end
--local function main()
tempProducter = {}
tempConsumer = {}
tempQueue = queue:new(PRODUCTERS) -- 生产者正在工作的队列
init(tempProducter, tempConsumer)
print("[main] start")
while true do
print("[main] enter 1 2 or exit")
local str = io.read()
if str == "exit" then
for i = 0, PRODUCTERS - 1 do
coroutine.resume(tempProducter[i]._co, tempProducter[i]._id, 1)
end
for i = 0, CONSUMERS - 1 do
coroutine.resume(tempConsumer[i]._co, tempConsumer[i]._id, 1)
end
tempQueue:clear()
tempQueue:deleteQueue()
break
end
if str == "1" then -- 方案 1
isFlag(0)
isFlag(1)
end
if str == "2" then -- 方案 2
for i = 1, 4 do
isFlag(0)
end
isFlag(1)
isFlag(0)
end
end
--main()
可以作其他的方案,提议idea:
(1)n个生产者一直在生产直到生产队列满,随机rand()睡眠,直到消费者进行消费队列有空闲的位置
(2)n个生产者一直在生产(生产过程:把产品插入队列,睡眠3s,此时 需要把该生产者放入一个生产中的标记数组作工作标记,直到生产过程结束再把该生产者放出数组),生产完继续唤起一个生产者继续生产,直到生产队列满的情况(此时生产者停止生产挂起,直到消费者进行消费队列有空闲的位置)
--伪代码
producter[i] 生产者标记数组 状态:free work
produce 产品
produceQueue 产品队列
producterDo()
-- do produce
producter[i] = work
producter.sleep(rand(n))
produceQueue:push(produce)
producter[i] = free
end
consumerDo()
-- do consumer
produceQueue:pop(produce)
end
productMain()
while true do
if produceQueue:isFull() then -- 产品队列是否满
coroutine.yield() -- 挂起等待下次唤起
end
if producter:isFree() then -- 生产者有没有空闲
coroutine.yield() -- 挂起等待下次唤起
end
producterDo()
coroutine.resume(consumerMain)-- 唤起消费者consumerMain 或者其他逻辑
end
end
consumerMain()
if produceQueue:empty() then -- 产品队列是否满
coroutine.yield() -- 挂起等待下次唤起
end
-- 有就进来消费
consumerDo()
coroutine.resume(productMain)-- 有空闲就唤起生产者可以生产 或者其他逻辑
end
而我正在学习的skynet也正好是这样子一种生产者与消费者的经典类型:
skynet是由事件驱动运行的,这里的事件主要就是两个,一个是socket,另一个是timeout。
分别由两个线程驱动运行。而内部的work线程就是调用skynet_context_message_dispatch去派发消息,派发完成后,它会挂起进入睡眠状态,等待前面两个线程来唤醒。