以前写过一编博客介绍我们游戏的AI服务器。
基本的结构就是利用windows的fiber,在每个fiber中运行一个lua虚拟机,具体的内容可以产参看
http://blog.csdn.net/sniperhuangwei/article/details/5425471
但这个方案有一个缺点,就是随着项目的推移,AI脚本变得越来越复杂,每个虚拟机占用的内存就变得越来越大。
当一个进程上运行的AI对象数量很大时这个进程就吃掉了非常大的内存。
经过一番考量,我决定不使用windows的fiber来支持用户态线程调度框架,而是改用lua的coroutine.
这样整个进程只需要维护一个lua虚拟机就够了,即使AI脚本变得更复杂所占用的内存也不会太恐怖.
下面是coroutine的调度框架,基本结构跟原来的fiber框架是一样的.
co.lua
coObject = { next_co = nil, --活动队列中的下一个协程对象 co = nil, --协程对象 status, --当前的状态 block = nil, --阻塞结构 timeout, index = 0, sc, --归属的调度器 name }
function coObject:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end
function coObject:init(name,sc,co) self.name = name self.sc = sc self.co = co end
function coObject:Signal(ev) if self.block ~= nil then if self.block:WakeUp(ev) then self.sc:Add2Active(self) end end end
blockStruct = { bs_type, }
function blockStruct:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end
function blockStruct:WakeUp(type) return true end
timer.lua
timer = { m_size = 0, --元素的数量 m_data = {} --元素 }
function timer:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end
function timer:Up(index) local parent_idx = self:Parent(index) while parent_idx > 0 do if self.m_data[index].timeout < self.m_data[parent_idx].timeout then self:swap(index,parent_idx) index = parent_idx parent_idx = self:Parent(index) else break end end end
function timer:Down(index) local l = self:Left(index) local r = self:Right(index) local min = index
if l <= self.m_size and self.m_data[l].timeout < self.m_data[index].timeout then min = l end
if r <= self.m_size and self.m_data[r].timeout < self.m_data[min].timeout then min = r end
if min ~= index then self:swap(index,min) self:Down(min) end end
function timer:Parent(index) return index/2 end
function timer:Left(index) return 2*index end
function timer:Right(index) return 2*index + 1 end
function timer:Change(co) local index = co.index if index == 0 then return end --尝试往下调整 self:Down(index) --尝试往上调整 self:Up(index) end
function timer:Insert(co) if co.index ~= 0 then return end self.m_size = self.m_size + 1 table.insert(self.m_data,co) co.index = self.m_size self:Up(self.m_size) end
function timer:Min() if self.m_size == 0 then return 0 end return self.m_data[1].timeout end
function timer:PopMin() local co = self.m_data[1] self:swap(1,self.m_size) self.m_data[self.m_size] = nil self.m_size = self.m_size - 1 self:Down(1) co.index = 0 return co end
function timer:Size() return self.m_size end
function timer:swap(idx1,idx2) local tmp = self.m_data[idx1] self.m_data[idx1] = self.m_data[idx2] self.m_data[idx2] = tmp
self.m_data[idx1].index = idx1 self.m_data[idx2].index = idx2 end
function timer:Clear() while m_size > 0 do self:PopMin() end self.m_size = 0 end
scheduler.lua
scheduler = { active_head = nil,--活动列表头 active_tail = nil,--活动列表尾 pending_add = {},--等待添加到活动列表中的coObject m_timer = nil, }
function scheduler:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end
function scheduler:init() self.m_timer = timer:new() end
--添加到活动列表中 function scheduler:Add2Active(coObj) table.insert(self.pending_add,coObj) end
--尝试唤醒uid function scheduler:TryWakeup(coObj,ev) if coObj.block then coObj:Signal(ev) end end
--强制唤醒纤程 function scheduler:ForceWakeup(coObj) if coObj.status ~= "ACTIVED" then coObj.sc:Add2Active(coObj) end
end
--强制唤醒阻塞在type条件上的纤程 function scheduler:ForceWakeup(coObj,type) if coObj.status ~= "ACTIVED" and coObj.block and coObj.block.bs_type == type then coObj.sc:Add2Active(coObj) end end
--睡眠ms function scheduler:Sleep(coObj,ms) if ms > 0 then coObj.timeout = GetTick() + ms if coObj.index == 0 then self.m_timer:Insert(coObj) else self.m_timer:Change(coObj) end coObj.status = "SLEEP" end coroutine.yield(coObj.co) end
--暂时释放执行权 function scheduler:Yield(coObj) coObj.status = "YIELD" coroutine.yield(coObj.co) end
--主调度循环 function scheduler:Schedule()
--将pending_add中所有coObject添加到活动列表中 for k,v in pairs(self.pending_add) do v.next_co = nil if self.active_tail ~= nil then self.active_tail.next_co = v self.active_tail = v else self.active_head = v self.active_tail = v end end
self.pending_add = {}
--运行所有可运行的coObject对象 local cur = self.active_head local pre = nil while cur ~= nil do coroutine.resume(cur.co,cur) print("从coro中回来") local status = cur.status --当纤程处于以下状态时需要从可运行队列中移除 if status == "DEAD" or status == "SLEEP" or status == "WAIT4EVENT" or status == "YIELD" then --删除首元素 if cur == self.active_head then --同时也是尾元素 if cur == self.active_tail then self.active_head = nil self.active_tail = nil else self.active_head = cur.next_co end elseif cur == self.active_tail then pre.next_co = nil self.active_tail = pre else pre.next_co = cur.next_co end
local tmp = cur cur = cur.next_co tmp.next_co = nil --如果仅仅是让出处理器,需要重新投入到可运行队列中 if status == "YIELD" then self:Add2Active(tmp) end else pre = cur cur = cur.next_co end end --看看有没有timeout的纤程 local now = GetTick() while self.m_timer:Min() ~=0 and self.m_timer:Min() <= now do local co = self.m_timer:PopMin() if co.status == "WAIT4EVENT" or co.status == "SLEEP" then self:Add2Active(co) end end end
test.lua
function cofun(coObj) while true do Show() print(coObj.name) coObj.sc:Sleep(coObj,1) end end
function test()
local sc = scheduler:new() sc:init()
local co1 = coObject:new() local coro1 = coroutine.create(cofun) co1:init("1",sc,coro1)
local co2 = coObject:new() local coro2 = coroutine.create(cofun) co2:init("2",sc,coro2)
local co3 = coObject:new() local coro3 = coroutine.create(cofun) co3:init("3",sc,coro3)
local co4 = coObject:new() local coro4 = coroutine.create(cofun) co4:init("4",sc,coro4)
sc:Add2Active(co1) sc:Add2Active(co2) sc:Add2Active(co3) sc:Add2Active(co4)
while true do sc:Schedule() end
end
|
请发表评论