--[[
-- added by wsh @ 2017-12-19
-- 协程模块:对Lua协程conroutine进行扩展,使其具有Unity侧协程的特性
-- 注意:
-- 1、主线程使用coroutine.start启动协程,协程启动以后,首次挂起时主线程继续往下执行,这里和Unity侧表现是一致的
-- 2、协程里可以再次使用coroutine.start启动协程,和在Unity侧协程中使用StartCoroutine表现一致
-- 3、协程里启动子级协程并等待其执行完毕,在Unity侧是yield return StartCoroutine,但是在Lua不需要另外启动协程,直接调用函数即可
-- 4、如果lua侧协程不使用本脚本的扩展函数,则无法实现分帧;lua侧启动协程以后不管协程函数调用栈多深,不管使用多少次本脚本扩展函数,都运行在一个协程
-- 5、使用coroutine.waitforframes(1)来等待一帧,千万不要用coroutine.yield,否则整个协程将永远不被唤醒===>***很重要,除非你在其它地方手动唤醒它
-- 6、子级协同在lua侧等同于普通函数调用,和普通函数一样可在退出函数时带任意返回值,而Unity侧协同不能获取子级协同退出时的返回值
-- 7、理论上任何协同都可以用回调方式去做,但是对于异步操作,回调方式也需要每帧去检测异步操作是否完成,消耗相差不多,而使用协同可以简单很多,清晰很多
-- 8、协程所有等待时间的操作,如coroutine.waitforseconds误差由帧率决定,循环等待时有累积误差,所以最好只是用于分帧,或者等待异步操作
-- 9、yieldstart、yieldreturn、yieldbreak实际上是用lua不对称协程实现对称协同,使用方式和Unity侧协同类似,注意点看相关函数头说明
-- TODO:
-- 1、CS侧做可视化调试器,方便单步查看各个协程运行状态
--]]-- 协程内部使用定时器实现,定时器是weak表,所以这里必须缓存Action,否则会被GC回收
local action_map = {}
-- action缓存池
local action_pool = {}
-- 用于子级协程yieldreturn时寻找父级协程
local yield_map = {}
-- 协程数据缓存池
local yield_pool = {}
-- 协程缓存池
local co_pool = {}-- 回收协程
local function __RecycleCoroutine(co)if not coroutine.status(co) == "suspended" thenerror("Try to recycle coroutine not suspended : "..coroutine.status(co))endtable.insert(co_pool, co)
end-- 可复用协程
local function __Coroutine(func, ...)local args = SafePack(...)while func dolocal ret = SafePack(func(SafeUnpack(args)))__RecycleCoroutine(coroutine.running())args = SafePack(coroutine.yield(SafeUnpack(ret)))func = args[1]table.remove(args, 1)end
end-- 获取协程
local function __GetCoroutine()local co = nilif table.length(co_pool) > 0 thenco = table.remove(co_pool)elseco = coroutine.create(__Coroutine)endreturn co
end-- 回收Action
local function __RecycleAction(action)action.co = falseaction.timer = falseaction.func = falseaction.args = falseaction.result = falsetable.insert(action_pool, action)
end-- 获取Action
local function __GetAction(co, timer, func, args, result)local action = nilif table.length(action_pool) > 0 thenaction = table.remove(action_pool)elseaction = {false, false, false, false, false}endaction.co = co and co or falseaction.timer = timer and timer or falseaction.func = func and func or falseaction.args = args and args or falseaction.result = result and result or falsereturn action
end-- 协程运行在保护模式下,不会抛出异常,所以这里要捕获一下异常
-- 但是可能会遇到调用协程时对象已经被销毁的情况,这种情况应该被当做正常情况
-- 所以这里并不继续抛出异常,而只是输出一下错误日志,不要让客户端当掉
-- 注意:Logger中实际上在调试模式会抛出异常
local function __PResume(co, func, ...)local resume_ret = nilif func ~= nil thenresume_ret = SafePack(coroutine.resume(co, func, ...))elseresume_ret = SafePack(coroutine.resume(co, ...))endlocal flag, msg = resume_ret[1], resume_ret[2]if not flag thenLogger.LogError(msg.."\n"..debug.traceback(co))elseif resume_ret.n > 1 thentable.remove(resume_ret, 1)elseresume_ret = nilendreturn flag, resume_ret
end-- 启动一个协程:等待协程第一次让出控制权时主函数继续往下执行,这点表现和Unity协程一致
-- 等同于Unity侧的StartCoroutine
-- @func:协程函数体
-- @...:传入协程的可变参数
local function start(func, ...)local co = __GetCoroutine()__PResume(co, func, ...)return co
end-- 启动一个协程并等待
-- 注意:
-- 1、这里会真正启动一个子级协程,比起在协程中直接函数调用开销稍微大点,但是灵活度很高
-- 2、最大好处就是可以在子级协程中使用yieldreturn返回值给父级协程执行一次某个回调,用于交付数据给父级协程
-- 3、如果子级协程没有结束,父级协程会在执行完回调之后等待一帧再次启动子级协程
-- 4、具体运用参考场景管理(ScenceManager)部分控制加载界面进度条的代码,十分清晰简洁
-- 5、如果不需要callback,即不需要子级协程交付数据,别使用yieldstart,直接使用普通函数调用方式即可
-- 6、回调callback函数体一般处理的是父级协程的逻辑,但是跑在子级协程内,这点要注意,直到yieldbreak父级协程都是挂起的
-- @func:子级协程函数体
-- @callback:子级协程在yieldreturn转移控制权给父级协程时,父级协程跑的回调,这个回调会填入子级协程yieldreturn时的参数
-- @...:传递给子级协程的可变参数
local function yieldstart(func, callback, ...)local co = coroutine.running() or error ('coroutine.yieldstart must be run in coroutine')local map = nilif table.length(yield_pool) > 0 thenmap = table.remove(yield_pool)map.parent = comap.callback = callbackmap.waiting = falsemap.over = falseelsemap = {parent = co, callback = callback, waiting = false, over = false}endlocal child = __GetCoroutine()yield_map[child] = maplocal flag, resume_ret = __PResume(child, func, ...)if not flag thentable.insert(yield_pool, map)yield_map[child] = nilreturn nilelseif map.over thentable.insert(yield_pool, map)yield_map[child] = nilif resume_ret == nil thenreturn nilelsereturn SafeUnpack(resume_ret)endelsemap.waiting = truelocal yield_ret = SafePack(coroutine.yield())table.insert(yield_pool, map)yield_map[child] = nilreturn SafeUnpack(yield_ret)end
end-- 子级协程将控制权转移给父级协程,并交付数据给父级协程回调,配合yieldstart使用
-- 注意:
-- 1、与Unity侧协程yield return不同,对子级协程来说yieldreturn一定会等待一帧再往下执行
local function yieldreturn(...)local co = coroutine.running() or error ("coroutine.yieldreturn must be run in coroutine")local map = yield_map[co]local parent = map.parent-- 没有父级协程,啥都不做if not map or not parent thenreturnendlocal callback = map.callbackassert(callback ~= nil, "If you don't need callback, use normal function call instead!!!")callback(co, ...)-- 子级协程等待一帧再继续往下执行return coroutine.waitforframes(1)
end-- 子级协程在异步回调中交付数据给父级协程回调,配合yieldstart使用
-- 注意:
-- 1、子级协程异步回调并没有运行在子级协程当中,不能使用yieldreturn,实际上不能使用任何协程相关接口,除了start
-- 2、yieldcallback需要传递当前的子级协程,这个可以从异步回调的首个参数获取
-- 3、不会等待一帧,实际上协程中的回调是每帧执行一次的
local function yieldcallback(co, ...)assert(co ~= nil and type(co) == "thread")local map = yield_map[co]-- 没有父级协程,啥都不做if not map or not map.parent thenreturnendlocal callback = map.callbackassert(callback ~= nil, "If you don't need callback, use normal function call instead!!!")callback(co, ...)
end-- 退出子级协程,将控制权转移给父级协程,并交付数据作为yieldstart返回值,配合yieldstart使用
-- 注意:
-- 1、一定要使用return coroutine.yieldbreak退出===>很重要***
-- 2、不使用coroutine.yieldbreak无法唤醒父级协程
-- 3、不使用return,可能无法正确退出子级协程
local function yieldbreak(...)local co = coroutine.running() or error ("coroutine.yieldbreak must be run in coroutine")local map = yield_map[co]-- 没有父级协程if not map thenreturn ...endmap.over = trueassert(map.parent ~= nil, "What's the fuck!!!")if not map.waiting thenreturn ...else__PResume(map.parent, nil, ...)end
endlocal function __Action(action, abort, ...)assert(action.timer)if not action.func thenabort = trueendif not abort and action.func thenif action.args and action.args.n > 0 thenabort = (action.func(SafeUnpack(action.args)) == action.result)elseabort = (action.func() == action.result)endendif abort thenaction.timer:Stop()action_map[action.co] = nil__PResume(action.co, ...)__RecycleAction(action)end
end-- 等待下次FixedUpdate,并在FixedUpdate执行完毕后resume
-- 等同于Unity侧的yield return new WaitForFixedUpdate
local function waitforfixedupdate()local co = coroutine.running() or error ("coroutine.waitforfixedupdate must be run in coroutine")local timer = TimerManager:GetInstance():GetCoFixedTimer()local action = __GetAction(co, timer)timer:Init(0, __Action, action, true, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 等待帧数,并在Update执行完毕后resume
local function waitforframes(frames)assert(type(frames) == "number" and frames >= 1 and math.floor(frames) == frames)local co = coroutine.running() or error ("coroutine.waitforframes must be run in coroutine")local timer = TimerManager:GetInstance():GetCoTimer()local action = __GetAction(co, timer)timer:Init(frames, __Action, action, true, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 等待秒数,并在Update执行完毕后resume
-- 等同于Unity侧的yield return new WaitForSeconds
local function waitforseconds(seconds)assert(type(seconds) == "number" and seconds >= 0)local co = coroutine.running() or error ("coroutine.waitforsenconds must be run in coroutine")local timer = TimerManager:GetInstance():GetCoTimer()local action = __GetAction(co, timer)timer:Init(seconds, __Action, action, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
endlocal function __AsyncOpCheck(co, async_operation, callback)if callback ~= nil thencallback(co, async_operation.progress)endreturn async_operation.isDone
end-- 等待异步操作完成,并在Update执行完毕resume
-- 等同于Unity侧的yield return AsyncOperation
-- 注意:yield return WWW也是这种情况之一
-- @async_operation:异步句柄---或者任何带有isDone、progress成员属性的异步对象
-- @callback:每帧回调,传入参数为异步操作进度progress
local function waitforasyncop(async_operation, callback)assert(async_operation)local co = coroutine.running() or error ("coroutine.waitforasyncop must be run in coroutine")local timer = TimerManager:GetInstance():GetCoTimer()local action = __GetAction(co, timer, __AsyncOpCheck, SafePack(co, async_operation, callback), true)timer:Init(1, __Action, action, false, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 等待条件为真,并在Update执行完毕resume
-- 等同于Unity侧的yield return new WaitUntil
local function waituntil(func, ...)assert(func)local co = coroutine.running() or error ("coroutine.waituntil must be run in coroutine")local timer = TimerManager:GetInstance():GetCoTimer()local action = __GetAction(co, timer, func, SafePack(...), true)timer:Init(1, __Action, action, false, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 等待条件为假,并在Update执行完毕resume
-- 等同于Unity侧的yield return new WaitWhile
local function waitwhile(func, ...)assert(func)local co = coroutine.running() or error ("coroutine.waitwhile must be run in coroutine")local timer = TimerManager:GetInstance():GetCoTimer()local action = __GetAction(co, timer, func, SafePack(...), false)timer:Init(1, __Action, action, false, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 等待本帧结束,并在进入下一帧之前resume
-- 等同于Unity侧的yield return new WaitForEndOfFrame
local function waitforendofframe()local co = coroutine.running() or error ("coroutine.waitforendofframe must be run in coroutine")local timer = TimerManager:GetInstance():GetCoLateTimer()local action = __GetAction(co, timer)timer:Init(0, __Action, action, true, true)timer:Start()action_map[co] = actionreturn coroutine.yield()
end-- 终止协程等待操作(所有waitXXX接口)
local function stopwaiting(co, ...)local action = action_map[co]if action then__Action(action, true, ...)end
endcoroutine.start = start
coroutine.yieldstart = yieldstart
coroutine.yieldreturn = yieldreturn
coroutine.yieldcallback = yieldcallback
coroutine.yieldbreak = yieldbreak
coroutine.waitforfixedupdate = waitforfixedupdate
coroutine.waitforframes = waitforframes
coroutine.waitforseconds = waitforseconds
coroutine.waitforasyncop = waitforasyncop
coroutine.waituntil = waituntil
coroutine.waitwhile = waitwhile
coroutine.waitforendofframe = waitforendofframe
coroutine.stopwaiting = stopwaiting-- 调试用:查看内部状态
if Config.Debug thenreturn{action_map = action_map,action_pool = action_pool,yield_map = yield_map,yield_pool = yield_pool,co_pool = co_pool,}
end

coroutine 携程与定时一起混用相关推荐

  1. Unity3D Coroutine携程管理器

    Unity3D Coroutine携程管理器 个人需求 使用效果 思路原理 代码实现 未来优化 在Unity3D的编程中携程一直占据很重要的位置,上到场景加载下到值渐变都有携程的参与. 但是目前网上对 ...

  2. 携程数据库高可用架构实践

    作者:携程技术团队,来自:DataFunTalk 导读: 我们推荐使用数据库三副本,一主一从一异地容灾.如果想要节省成本,也可以只保留两副本,但是一旦其中一台服务器发生故障,服务器维修时间会比较长,那 ...

  3. 携程python_Python 携程

    一.协程 1.又称微线程,纤程.英文名Coroutine.一句话说明什么是协程:协程是一种用户态的轻量级线程(相当于操作系统不知道它的存在,是用户控制的). 2.协程拥有自己的寄存器上下文和栈(代码的 ...

  4. 强化学习在携程酒店推荐排序中的应用探索

    https://www.toutiao.com/a6708585355555111431/ 目前携程酒店绝大部分排序业务中所涉及的问题,基本可以通过应用排序学习完成.而其中模型训练步骤中所需的训练数据 ...

  5. 干货 | 携程基于Quasar协程的NIO实践

    作者简介 Ryan,携程Java开发工程师,对高并发.网络编程等领域有浓厚兴趣. IO密集型系统在高并发场景下,会有大量线程处于阻塞状态,性能低下,JAVA上成熟的非阻塞IO(NIO)技术可解决该问题 ...

  6. 干货 | 携程异地多活-MySQL实时双向(多向)复制实践

    作者简介 Roy,携程软件技术专家,负责MySQL双向同步DRC和数据库访问中间件DAL的开发演进,对分布式系统高可用设计.数据一致性领域感兴趣. 一.前言 携程内部MySQL部署采用多机房部署,机房 ...

  7. 干货 | 数万实例数百TB数据量,携程Redis治理演进之路

    作者简介 向晨,携程资深数据库工程师:布莱德,携程技术专家:皓月,携程技术培训生: 一.背景 携程自2013年开始使用Redis,旧时期为Memcached和Redis混用状态.由于Redis在处理性 ...

  8. 干货 | 强化学习在携程酒店推荐排序中的应用探索

    宣云儿,携程酒店排序算法工程师,主要负责酒店排序相关的算法逻辑方案设计实施.目前主要的兴趣在于排序学习.强化学习等领域的理论与应用. 前言 目前携程酒店绝大部分排序业务中所涉及的问题,基本可以通过应用 ...

  9. 腾讯 VS 阿里 VS 携程消息中间件设计方案及思路

    目标:可靠性(保证消息不丢失).异步.解耦(无需同时在线.不需要知道对方是谁). 数据的存储级别:内存中的数据(断电丢数据)=>持久化磁盘(磁盘损坏)=>冗余备份(一致性问题) 业界MQ设 ...

最新文章

  1. 转 linux进程内存到底怎么看 剖析top命令显示的VIRT RES SHR值
  2. HTML5音乐播放器(四):播放列表与播放方式
  3. PMCAFF | 一个CRM产品大神的产品之路
  4. linux删除某个path_linux 从path中去掉某个
  5. linux vi使用手册,史上最全VIM使用手册
  6. vim怎么跳转到函数定义处_Vim、gvim操作跳转光标区块和代码块的跳转
  7. 利用Eigen进行矩阵计算
  8. overleaf换模板
  9. 私塾在线《研磨设计模式》,精品课程上线特大惊喜
  10. 1252 :[蓝桥杯2015初赛]奇妙的数字 C/C++
  11. IT知识点及书籍推荐
  12. 2018.3.4 st
  13. virtualbox安装mac os x雪豹
  14. 问题记录-笔记本HDMI外接2k显示器如何调2k分辨率
  15. android 耳机孔 红外,手机遥控器,3.5mm耳机接口红外遥控改造解析
  16. Ubuntu Kylin 20_10 在VMware Workstation Pro上安装
  17. linux modprobe命令参数
  18. 使用selenium模拟登录解决滑块验证问题
  19. DD 摆磁铁(计蒜客信息学8月普及组模拟赛)
  20. 模型优化与tensorflow

热门文章

  1. uniapp调用地图,进行位置查询,标记定位
  2. 有关Java的银行家算法
  3. QSettings 读写ini文件
  4. 别再自己抠图了,Python用5行代码实现批量抠图
  5. python的分类算法有哪些_python数据挖掘中的分类算法有哪些?
  6. 安卓Android Studio布局文件分类存放,java文件分类存放
  7. android gdx 放进布局,将LibGDX添加到现有的Android Studio项目中
  8. ubuntu如何在开发板上挂载文件
  9. 一个用Dockerfile建立镜像的模板,和使用说明
  10. 真正解决百度定位只定位一次的问题