【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去派发消息,派发完成后,它会挂起进入睡眠状态,等待前面两个线程来唤醒。
【lua学习】协程 元表 处理多个生产者与消费者的问题 应用skynet消息传递_第1张图片

你可能感兴趣的:(协程,闭包,lua)