【lua學習】協程 元表 處理多個生產者與消費者的問題
之前寫了隊列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也正好是這樣子一種生產者與消費者的經典類型:
skynet是由事件驅動運行的,這裏的事件主要就是兩個,一個是socket,另一個是timeout。
分別由兩個線程驅動運行。而內部的work線程就是調用skynet_context_message_dispatch去派發消息,派發完成後,它會掛起進入睡眠狀態,等待前面兩個線程來喚醒。