skynet笔记——服务调度,获取时间与错误处理(四)

服务调度
skynet笔记——服务调度,获取时间与错误处理(四)_第1张图片


使用sleep休眠

示例代码:testsleep.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第2张图片
运行结果(还是先运行main.lua):
skynet笔记——服务调度,获取时间与错误处理(四)_第3张图片
在console服务中输入 testsleep 之后,马上再输入 test ,会发现, test 服务不会马上启动,因为这个时候console正在忙于第一个服务 testsleep 初始化,需要等待5秒钟之后,输入的 test 才会被console处理。

注意:以上做法是不正确的,在 skynet.start 函数中的服务初始化代码不允许有阻塞函数的存在,服务的初始化要求尽量快的执行完成,所有的业务逻辑代码不会写在 skynet.start 里面。


在服务中开启新的线程

在skynet的服务中我们可以开一个新的线程来处理业务(注意这个的线程并不是传统意义上的线程,更像是一个
虚拟线程,其实是通过协程来模拟的
)。

示例代码: testfork.lua

local skynet = require "skynet"

function task(timeout)
	skynet.error("fork co:", coroutine.runing())
	skynet.error("begin sleep")
	skynet.sleep(timeout)
	
	skynet.error("begin end")
end

skynet.start(function ()
	skynet.error("start co:", coroutine.runing())
	skynet.fork(task, 500) --开启一个新的线程来执行task任务
	--skynet.fork(task, 500) --再开启一个新的线程来执行task任务
end)

运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第4张图片
可以看到在 testfork 启动后, consloe 服务仍然可以接受终端输入的 test ,并且启动。

以后如果遇到需要长时间运行,并且出现阻塞情况,都要使用 skynet.fork 在创建一个新的线程(协程)。

查看源码skynet.lua了解底层实现,其实就是使用coroutine.create实现

每次使用skynet.fork其实都是从协程池中获取未被使用的协程,并把该协程加入到fork队列中,等待一个消息调度,然后会依次把fork队列中协程拿出来执行一遍,执行结束后,会把协程重新丢入协程池中,这样可以避免重复开启关闭协程的额外开销。


长时间占用执行权限的任务

示例代码:busytask.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第5张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第6张图片
上面的运行结果充分说明了, skynet.fork 创建的线程其实通过lua协程来实现的,即一个协程占用执行权后,其他的协程需要等待。


使用skynet.yield让出执行权

示例代码:testyield.lua
在这里插入图片描述
skynet笔记——服务调度,获取时间与错误处理(四)_第7张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第8张图片
通过使用 skynet.yield() 然后同一个服务中的不同线程都可以得到执行权限。


线程间的简单同步

同一个服务之间的线程可以通过, skynet.wait 以及 skynet.wakeup 来同步线程

示例代码:testwakeup.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第9张图片
需要注意的是:skynet.wakeup除了能唤醒wait线程,也可以唤醒sleep的线程。


定时器的使用

skynet中的定时器,其实是通过给定时器线程注册了一个超时时间,并且占用了一个空闲协程,空闲协程也是从协程池中获取,超时后会使用空闲协程来处理超时回调函数。

启动一个定时器

示例代码:testtimeout.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第10张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第11张图片
其实skynet.start服务启动函数实现中,就已经启动了一个timeout为0s的定时器,来执行通过skynet.start函数传参得到的初始化函数。其目的是为了让skynet工作线程调度一次新服务。这一次服务调度最重要的意义在于把fork队列中的协程全部执行一遍。

循环启动定时器
skynet笔记——服务调度,获取时间与错误处理(四)_第12张图片
执行结果,交替使用协程池中的协程:
skynet笔记——服务调度,获取时间与错误处理(四)_第13张图片


获取时间与错误处理

获取时间示例代码:testtime.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第14张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第15张图片
lua中的错误处理都是通过assert以及error来抛异常,并且中断当前流程,skynet也不例外,但是你真的懂assert以及error吗?

注意这里的error不是skynet.error,skynet.error单纯写日志的,并不会中断流程。

skynet中使用assert或error后,服务会不会中断,skynet节点会不会中断?

来看一个例子:testassert.lua
skynet笔记——服务调度,获取时间与错误处理(四)_第16张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第17张图片
上面的结果已经很明显了,开了两个协程分别执行task1、task2,task1断言后终止掉当前协程,不会再往下执行,但是task2还是能正常执行。skynet节点也没有挂掉,还是能正常运行。

那么我们在处理skynet的错误的时候可以大胆的使用assert与error,并不需要关注错误。当然,一个好的服务端,肯定不能一直出现中断掉的协程。

如果不想把协程中断掉,可以使用pcall来捕捉异常,例如:
在这里插入图片描述
skynet笔记——服务调度,获取时间与错误处理(四)_第18张图片
skynet笔记——服务调度,获取时间与错误处理(四)_第19张图片
运行结果:
skynet笔记——服务调度,获取时间与错误处理(四)_第20张图片

你可能感兴趣的:(skynet)