lua 协程 | 协程实现消息机制(事件队列轮询处理机制)
来源:互联网 发布:2016新开淘宝运营教程 编辑:程序博客网 时间:2024/05/29 17:32
1 协程基础知识
Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
协程有三种状态:挂起,运行,停止。创建后是挂起状态,即不自动运行。status函数可以查看当前状态。协程可通过yield函数将一段正在运行的代码挂起。
lua的resume-yield可以互相交换数据。如果没有对应的yield,传递给resume的额外参数将作为参数传递给协程主函数:
co = coroutine.create(function (a, b, c) print("co", a, b, c)end)coroutine.resume(co, 1, 2, 3)运行结果: co 1 2 3如果没有错误的话,resume将返回true和yield的参数,弱出现错误,返回false与相关错误信息:
co = coroutine.create(function (a, b) coroutine.yield(a+b, a-b)end)print(coroutine.resume(co, 3, 8))运行结果: true 11 5同样地,yield也将返回由对应的resume传递而来的参数:
co = coroutine.create (function ()print("begin coroutine")print("co", coroutine.yield())end)coroutine.resume(co) -- 遇到coroutine.yield()挂起coroutine.resume(co, 4, 5) -- 完成第二个print过程运行结果: begin coroutineco 4 5协程主函数返回值将作为与之对应的resume的返回值(第一个参数是true):
co = coroutine.create(function (a)return 6, 7, coroutine.yield(a-1)end)print(coroutine.resume(co,5)) -- 输出true与coroutine.yield()运行参数print(coroutine.resume(co,5)) -- 输出协程主函数返回值运行结果: true4true 6 7 5
下面用协同程序实现一个典型的生产者-消费者模式。
function receive(prod)local status,value = coroutine.resume(prod) return valueendfunction send( x )coroutine.yield(x) -- 挂起,返回当前协同程序 供resume调用endfunction produce() -- 生产者return coroutine.create(function ()while true do -- 该循环保证了生产者被无限驱动local x = io.read()send(x)endend)endfunction consume(prod) -- 消费者while true dolocal x = receive(prod) -- 消费者驱动生产者的执行print(x)endendfunction filter( prod ) -- 用过滤器处理生产者数据return coroutine.create(function ()while true dolocal x = receive(prod) -- 驱动生产者生成数据x = doSomeProcess(x)send(x)endend)endconsume(filter(produce()))
我们尝试用协程来实现一些算法和应用级的效果。
1)用生产者-消费者模式实现迭代器,该迭代器能输出一个数组的全排列。
function permgen( a,n ) -- 生产者if n <= 0 thencoroutine.yield(a)elsefor i=1,n doa[i],a[n] = a[n],a[i]permgen(a,n-1)a[i],a[n] = a[n],a[i]endendendfunction perm( a ) -- 迭代器 消费者return coroutine.wrap(function ()permgen(a,#a)end)--[[-- 与上面代码效果相同local prod = coroutine.create(function ()permgen(a,#a)end)return function ()local code,rt = coroutine.resume(prod)return rtend]]endlocal a = {1,2,3}for v in perm(a) dolocal st = ""for i,v in ipairs(v) dost = st .. v .. " "endprint(st)end
2)用协程实现一个多文件下载分发器
有多个文件需要下载,如果采用顺序排队方式,那么大量时间将浪费在 请求下载-下载完成反馈之间的等待过程中。我们用协程解决这一效率问题:请求下载后,如果超时则yield挂起当前协程,并有分发器调用其他协程。
download = function (host,file)local c = socket.connect(host,80)c:send("GET " .. file .. "HTTP/1.0\r\n") -- 发送请求while true dolocal s,status = receive(c) -- 接收if status == "closed" then-- 当前协程的下载过程已完成breakendendendreceive = function (connection)local s,status = connection:receive()if status == "timeout" thencoroutine.yield(connection)end return s,statusenddispatch = function ()while true do if #loadLst <= 0 then-- 所有下载器均完成 结束分发breakendfor i,v in ipairs(loadLst) dolocal status,res = coroutine.resume(v)if not res thentable.remove(loadLst,i) -- 当前下载器完成breakendendendend
2 协同实现消息机制(不同场景等待式消息处理)
游戏中,有时在处理消息时,希望一条一条消息独立处理(独立堆栈),且希望每条消息在不同场景内等待式逐步进行(如一个场景消息处理完,挂起,经过100ms再进行当前消息下一场景的处理),协程能够完成这一过程。下面提供一种实现方案。
local msgLst = {} -- 存储local curMsgCor = nil -- 当前消息对应的协程function insertPerMsg( msg ) table.insert(msgLst,msg) scheduleScript(processMsg) -- -- 定时器中循环执行函数endfunction processMsg( ) if #msgLst <= 0 then unScheduleScript(processPerMsgCor) else if not curMsgCor then local curMsg = table.remove(msgLst,1) curMsgCor = coroutine.create(function () processPerMsgCor(curMsg) end) -- 创建coroutine end if curMsgCor then local state,errMsg = coroutine.resume(curMsgCor) -- 重启coroutine local status = coroutine.status(curMsgCor) -- 查看coroutine状态: dead,suspend,running -- 启动失败 if not state then curMsgCor = nil local debugInfo = debug.traceback(curMsgCor) print(debugInfo) end -- coroutine消亡 if status == "dead" then curMsgCor = nil end end endendfunction processPerMsgCor( curMsg ) processForTalk() coroutine.yield() -- 挂起coroutine processForSpecialRoom() coroutine.yield() processForOtherWay()end
3 事件队列轮询处理机制
游戏场景经常需要一次执行一系列事件,每个事件的完成均需要一定时间。如需要在奔跑至指定区域后释放技能或攻击某一对象。可通过事件队列方式完成这一过程。
local Event = class("Event")Event.State = {None = 1,Doing = 2,Done = 3,}function Event:ctor()self.state = self.State.Noneendfunction Event:isNotStart()return self.state == self.State.Noneendfunction Event:setDoing()self.state = self.State.Doingendfunction Event:setFunc( func,... )self.func = funcself.funcParams = {...}endfunction Event:doFunc()self.func(self,unpack(self.funcParams))end-- Func完成后调用function Event:setDone()self.state = self.State.Doneendfunction Event:isDone()return self.state == self.State.Doneendfunction Test:ctor( )-- 创建事件队列self.eventQueue = Queue:create()-- 定时器轮询时间事件队列local schedule = cc.Director:getInstance():getScheduler()self.scheduleId = schedule:scheduleScriptFunc(handler(self,self.runEventQueue),1,false)-- 添加几个事件local deltaTime = cc.DelayTime:create(5)local event = Event:create()-- func中的参数event在执行doFunc时传入local func = function (event)local f1 = function()-- 5秒后输出print("print this after 5 sec")event:setDone()endself:runAction(cc.Sequence:create(deltaTime,cc.CallFunc:create(f1)))endevent:setFunc(func)self:addEvent(event)event = Event:create()func = function (event)local f1 = function()-- 10秒后输出print("print this after 10 sec")event:setDone()endself:runAction(cc.Sequence:create(deltaTime,cc.CallFunc:create(f1)))endevent:setFunc(func)self:addEvent(event)event = Event:create()func = function (event)local f1 = function()-- 15秒后输出print("print this after 15 sec")event:setDone()endself:runAction(cc.Sequence:create(deltaTime,cc.CallFunc:create(f1)))endevent:setFunc(func)self:addEvent(event)endfunction Test:addEvent(event)self.eventQueue:push_back(event)endfunction Test:runEventQueue()if self.eventQueue:size() <= 0 thenreturnend-- 两种写法--[[ 第一种写法:完成当前事件后等待计时器执行下一个事件,事件之间存在时间间隙local curEvent = self.eventQueue:front()if curEvent and not curEvent:isDoing() thenif curEvent:isNotStart() then-- 执行事件curEvent:setDoing()curEvent:doFunc()elseif curEvent:isDone() then-- 事件已完成,删除并转至下个事件self.eventQueue:pop_front()curEvent = nilif self.eventQueue:size() > 0 thencurEvent = self.eventQueue:front()endendend]]-- 第二种写法:当前时间完成即刻执行下一时间while curEvent and not curEvent:isDoing() doif curEvent:isNotStart() then-- 执行事件curEvent:setDoing()curEvent:doFunc()elseif curEvent:isDone() then-- 事件已完成,删除并转至下个事件self.eventQueue:pop_front()curEvent = nilif self.eventQueue:size() > 0 thencurEvent = self.eventQueue:front()endendendend
0 0
- lua 协程 | 协程实现消息机制(事件队列轮询处理机制)
- lua实现c#事件机制
- Looper中的消息队列处理机制
- Looper中的消息队列处理机制
- uC/OSIII的消息队列处理机制
- Android Looper消息队列处理机制
- Windows 消息处理机制与事件驱动
- Windows 消息处理机制与事件驱动
- 事件处理方式 && 消息传递机制
- IPC实现机制(三)---消息队列
- 事件处理机制之Handler消息传递机制浅析
- Android 事件分发机制 和 消息处理机制
- 消息队列机制
- 消息队列机制
- android消息队列机制
- 消息队列机制
- linux 消息队列机制
- 消息机制理解-消息队列
- u-boot与Linux内核间的参数传递过程分析[转载]
- Vs + Qt 各种路径设置
- 二叉树中和为某一值的路径(java版)
- Gradle基础
- ListView缓存优化机制
- lua 协程 | 协程实现消息机制(事件队列轮询处理机制)
- sqlite简单封装
- nginx会去掉带有下划线的Header键值
- samba映射linux驱动器
- Struts2拦截器登录验证
- Android探索之旅(第十七篇)Android中使用webSocket保持长连接通信
- mysql-mac下载安装及修改初始密码
- Gym
- json数据转换为xml格式