【lua學習】協程 元表 處理多個生產者與消費者的問題 應用skynet消息傳遞

【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去派發消息,派發完成後,它會掛起進入睡眠狀態,等待前面兩個線程來喚醒。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章