初试定时器
新入门skynet系列视频b站网址 https://www.bilibili.com/video/BV19d4y1678X
skynet里面提供了定时器。
skynet.timeout
实际上是请求定时器线程 往自己的队列添加一个消息。首先会向系统注册一个定时器,然后获取一个协程。当定时器触发时,通过定时器的session找到对应的协程,并执行这个协程。
function skynet.timeout(ti, func)
local session = c.intcommand("TIMEOUT",ti) --seesion是通过当前服务分配的
assert(session)
local co = co_create_for_timeout(func, ti) -- 获取一个协程 注意设置了任务函数
assert(session_id_coroutine[session] == nil)
session_id_coroutine[session] = co --建立session 和 协程的 对应关系
return co -- for debug
end
上面行2 转到下面的c代码
static const char *
cmd_timeout(struct skynet_context * context, const char * param) {
char * session_ptr = NULL;
int ti = strtol(param, &session_ptr, 10);
int session = skynet_context_newsession(context);//获取一个服务内唯一的 session
skynet_timeout(context->handle, ti, session);//去注册一个定时器
sprintf(context->result, "%d", session);
return context->result;
}
当定时器触发时,会向当前服务所在的队列push一个响应消息。我们看处理这个消息的过程。当然lua层的处理函数入口是 skynet.dispatch_message
function skynet.dispatch_message(...)
local succ, err = pcall(raw_dispatch_message,...)-- next
while true do
if fork_queue.h > fork_queue.t then
-- queue is empty
fork_queue.h = 1 --head
fork_queue.t = 0 --tail
break
end
-- pop queue
local h = fork_queue.h
local co = fork_queue[h]
fork_queue[h] = nil
fork_queue.h = h + 1
local fork_succ, fork_err = pcall(suspend,co,coroutine_resume(co))
end
assert(succ, tostring(err))
end
我们看 第2行代码的 raw_dispatch_message函数
local function raw_dispatch_message(prototype, msg, sz, session, source)
-- skynet.PTYPE_RESPONSE = 1, read skynet.h
if prototype == 1 then --这里是处理响应
local co = session_id_coroutine[session] --响应消息的处理都是 通过seesion去得到协程 然后执行协程;因为session是本服务分配的 所以具有唯一性
session_id_coroutine[session] = nil
suspend(co, coroutine_resume(co, true, msg, sz, session))
else --这里主要是处理lua text socket 等消息类型
--略
end
end
通过第4行我们找到协程。第6行唤醒协程开始执行 任务函数。
关于定时器的内部实现详细分析,会在 定时器底层实现 里面讲到
take it easy