lua服务在执行回调函数的过程中,调用某些函数会挂起协程,比如skynet.call, skynet.ret, skynet.response等等,这些函数把协程挂起后,如何唤醒呢?

本文将对所有调用coroutine.yield的API的唤醒做下分析。(比较拗口,找不到更好的表达方式了)

skynet.call

function skynet.call(addr, typename, ...)local p = proto[typename]local session = c.send(addr, p.id , nil , p.pack(...))if session == nil thenerror("call to invalid address " .. skynet.address(addr))endreturn p.unpack(yield_call(addr, session))
end

功能:发起了一次 RPC ,并阻塞等待回应。

唤醒方式:目标服务调用skynet.ret, 返回一个PTYPE_RESPONSE类型的消息,在raw_dispatch_message函数内,会专门对这类消息做特殊处理,从之前缓存的表(key是session)中取出co, 然后再resume。

详细过程可以参考这篇文章:skynet.call流程

skynet.rawcall

function skynet.rawcall(addr, typename, msg, sz)local p = proto[typename]local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address")return yield_call(addr, session)
end

它和 skynet.call 功能类似(也是阻塞 API)。但发送时不经过 pack 打包流程,收到回应后,也不走 unpack 流程。

所以其唤醒方式同skynet.call。

skynet.ret

function skynet.ret(msg, sz)msg = msg or ""return coroutine_yield("RETURN", msg, sz)
end

此函数的wiki文档:

回应一个消息可以使用 skynet.ret(message, size) 。它会将 message size 对应的消息附上当前消息的 session ,以及 skynet.PTYPE_RESPONSE 这个类别,发送给当前消息的来源 source 。由于某些历史原因(早期的 skynet 默认消息类别是文本,而没有经过特殊编码),这个 API 被设计成传递一个 C 指针和长度,而不是经过当前消息的 pack 函数打包。或者你也可以省略 size 而传入一个字符串。

由于 skynet 中最常用的消息类别是 lua ,这种消息是经过 skynet.pack 打包的,所以惯用法是 skynet.ret(skynet.pack(...)) 。btw,skynet.pack(…) 返回一个 lightuserdata 和一个长度,符合 skynet.ret 的参数需求;与之对应的是 skynet.unpack(message, size) 它可以把一个 C 指针加长度的消息解码成一组 Lua 对象。

skynet.ret 在同一个消息处理的 coroutine 中只可以被调用一次,多次调用会触发异常。有时候,你需要挂起一个请求,等将来时机满足,再回应它。而回应的时候已经在别的 coroutine 中了。针对这种情况,你可以调用 skynet.response(skynet.pack) 获得一个闭包,以后调用这个闭包即可把回应消息发回。这里的参数 skynet.pack 是可选的,你可以传入其它打包函数,默认即是 skynet.pack 。

关键代码

function skynet.ret(msg, sz)msg = msg or ""return coroutine_yield("RETURN", msg, sz)
end-- 上面yield "RETURN" 后,会走到这里来
function suspend(co, result, command, param, size)...if command == "RETURN" thenlocal co_session = session_coroutine_id[co]local co_address = session_coroutine_address[co]if param == nil or session_response[co] thenerror(debug.traceback(co))endsession_response[co] = truelocal retif not dead_service[co_address] thenret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) ~= nilif not ret then-- If the package is too large, returns nil. so we should report error backc.send(co_address, skynet.PTYPE_ERROR, co_session, "")endelseif size ~= nil thenc.trash(param, size)ret = falseendreturn suspend(co, coroutine.resume(co, ret))end...
end

可以看出,这个函数在调用yield后,suspend函数会马上调用resume唤醒它,所以此函数是非阻塞 API 。


使用心得:
一般的使用习惯是把skynet.ret作为回调函数的最后一句,在这句执行完后,整个回调函数就结束了,其协程也将被回收。

类似这样:

skynet.start(function()skynet.dispatch("lua", function(session, address, cmd, ...)local f = command[string.upper(cmd)]if f thenskynet.ret(skynet.pack(f(...)))elseerror(string.format("Unknown command %s", tostring(cmd)))endend)skynet.register "SIMPLEDB"
end)

skynet.response

function skynet.response(pack)pack = pack or skynet.packreturn coroutine_yield("RESPONSE", pack)
end

这个函数的唤醒方式同ret一样,也是在suspend函数内重新唤醒。
同样也是非阻塞API。

关于此函数更详细的分析,请参考这篇文章:skynet.response分析

skynet.sleep

参考。。

skynet.wait

参考。。

skynet.exit

todo

function skynet.exit()fork_queue = {} -- no fork coroutine can be execute after skynet.exitskynet.send(".launcher","lua","REMOVE",skynet.self(), false)-- report the sources that call mefor co, session in pairs(session_coroutine_id) dolocal address = session_coroutine_address[co]if session~=0 and address thenc.redirect(address, 0, skynet.PTYPE_ERROR, session, "")endendfor resp in pairs(unresponse) doresp(false)end-- report the sources I call but haven't returnlocal tmp = {}for session, address in pairs(watching_session) dotmp[address] = trueendfor address in pairs(tmp) doc.redirect(address, 0, skynet.PTYPE_ERROR, 0, "")endc.command("EXIT")-- quit servicecoroutine_yield "QUIT"
end

lua服务执行过程中协程的挂起和重新唤醒相关推荐

  1. python协程库_python中协程的详解(附示例)

    本篇文章给大家带来的内容是关于python中协程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 协程,又称微线程,纤程.英文名Coroutine 协程看上去也是子程序 ...

  2. Unity中协程(IEnumerator)的使用方法介绍

    在Unity中,一般的方法都是顺序执行的,一般的方法也都是在一帧中执行完毕的,当我们所写的方法需要耗费一定时间时,便会出现帧率下降,画面卡顿的现象.当我们调用一个方法想要让一个物体缓慢消失时,除了在U ...

  3. Go中协程间通信的方式Sync.Cond

    在Go中协程间通信的方式有多种,最常用的是channel.如果牵扯多个协程的通知,可以使用sync.Cond. 1. 程序中的通信方式 GO语言中有句名言:"不要用共享内存来通信,而是使用通 ...

  4. 一个进程在执行过程中可以被中断事件打断_Linux操作系统:中断类型和中断的作用...

    1.中断的概念 中断对于操作系统非常重要,它就好像机器中的齿轮,驱动各部件的动作.所以,许多人称操作系统是由"中断驱动"的. 所谓中断是指CPU对系统发生的某个事件做出的一种反应, ...

  5. Kotlin中协程理解与实战(一)

    Kotlin中协程理解与实战(一) 什么是协程 在Android中协程用来解决什么问题 协程是: suspend -也称为挂起或暂停,用于暂停执行当前协程,并保存所有局部变量: resume -用于让 ...

  6. 【Python】【入门篇】十二、Python中协程

    目录 十二.Python中协程 12.1 协程的定义 12.2 协程 12.3 协程池 12.4 总结 十二.Python中协程 12.1 协程的定义 协程(Coroutine):是一种比线程更加轻量 ...

  7. DRG专业分组器-预分组在医院DRG付费政策执行过程中的作用(分组器+前置控制)支持中国所有使用DRG付费城市,准确率接近100%

    本文中对本地分组简写为DRG-LOCAL. DRGs(Diagnosis Related Groups)的中文全称是"按疾病诊断相关分组",是将住院病人按照临床相似性以及资源消耗相 ...

  8. linux脚本执行过程中被挂起,Linux学习笔记(八)——脚本执行的过程控制

    一.脚本执行过程中的控制 之前的内容中,运行编写好的脚本时都是在命令行上直接确定运行的,并且运行的脚本是实时的,这并不是脚本唯一的运行方式,下面的内容是脚本的其他运行方式.例如在Linux系统中如何控 ...

  9. Sql Server 因为触发器问题导致数据库更新报错“在触发器执行过程中引发了错误,批处理已中止”的问题处理...

    在维护一个非常旧的项目时,由于该项目版本已经非常老了,而且在客户现场运行的非常稳定,更要命的是本人目前没有找到该项目的代码,为了处理一个新的需求而且还不能修改程序代码,于是决定从数据库入手,毕竟该项目 ...

最新文章

  1. “诺奖风向标”--2020年斯隆研究奖公布,其中有16位华人学者获奖!
  2. [GDUT 决赛]--GCD,LCM——我是好人(数论)
  3. koa+mysql+vue+socket.io全栈开发之web api篇
  4. [Android Studio 权威教程]Windows下安装Android Studio
  5. 硬件产品研发,除了电子元器件成本,还有什么成本?
  6. 逆波兰计算器android源码简书,汪都能理解的逆波兰计算器(C++实现)
  7. 完美!解决无法启动承载网络的问题
  8. 关于zend framework控制器中action命名的问题
  9. java循环隔行变色_c:foreach标签详解----(隔行换背景颜色的问题)
  10. 【ASP.NET】swfuplod图片上传
  11. 2020超星android测试,2020知到《现代物流学》免费答案超星尔雅《测试作业导入》答案公众号...
  12. Android逆向工程:MIUI系统大揭秘:去不掉的小米账号!
  13. Java批量png转jpg图片格式
  14. 雅思核心词拾遗02----Family+ Law
  15. .bat批处理文件格式运行JAVA应用程序
  16. Java - Js 谷歌浏览器(Chrome)调用Ie浏览器
  17. 【虚幻引擎4(UE4)实用技巧】-关于高亮显示物体轮廓线
  18. word2vec python实现
  19. 网络Sniffing原理
  20. 极光短信在程序中(JAVA)的使用

热门文章

  1. 魅族手机怎么升级Android版本,魅族魅蓝Note的手机系统是什么?能升级安卓5.0吗?...
  2. 面向对象:期待正当年华,与你欣喜相见
  3. 新大陆软件公司校招面试(个人经历)
  4. 第1章 认识数据库
  5. 一个超好用的论文图片编辑神器---Inkscape(Mark)
  6. 海尔U+与土曼为何能达成“不对称合作”?
  7. php数据库中创建视图,视图创建
  8. 服务器上出现应用程序错误。此应用程序的当前自定义错误设置禁止远程查看应用程序错误的详细信息(出于安全原因)。未能加载文件或程序集“XXXXXX”或它的某一个依赖项。拒绝访问。
  9. 只改一个值 加快宽带上网速度
  10. lru调度算法例题_lru算法(lru算法及例题讲解)