话不多说,先贴出测试代码
local skynet = require("skynet")
function task( )
skynet.error("start sleep:",coroutine.running()) --被唤醒 然后sleep 5s
skynet.sleep(500)
skynet.error("end sleep:",coroutine.running())
end
skynet.start(function( )
skynet.error("start:",coroutine.running())
local co = skynet.fork(task) --fork 完了立即返回 co等待被唤醒执行task
skynet.error("co status:",coroutine.status(co))
end)
start函数的调用这里就不分析了,前面有介绍过,下面直接讨论skynet.fork、以及task的调用过程
//skynet.fork 调用
function skynet.fork(func,...)
local args = table.pack(...)
local co = co_create(function() //创建一个协程,相当于创建一个lua虚拟机
func(table.unpack(args,1,args.n))
end)
table.insert(fork_queue, co) //将协程加到fork_queue队列中
return co
end
那fork_queue中挂起的协程什么时候被唤醒呢?
那就要回到 如下图
coroutine_resume 回去唤醒对应的co,若这次用的是协程池里面的co,接下来分析一下过程
local function co_create(f)
local co = table.remove(coroutine_pool)
if co == nil then
co = coroutine_create(function(...)
f(...)
while true do
local session = session_coroutine_id[co]
if session and session ~= 0 then
local source = debug.getinfo(f,"S")
skynet.error(string.format("Maybe forgot response session %s from %s : %s:%d",
session,
skynet.address(session_coroutine_address[co]),
source.source, source.linedefined))
end
-- coroutine exit
local tag = session_coroutine_tracetag[co]
if tag ~= nil then
if tag then c.trace(tag, "end") end
session_coroutine_tracetag[co] = nil
end
local address = session_coroutine_address[co]
if address then
release_watching(address)
session_coroutine_id[co] = nil
end
-- recycle co into pool
f = nil
coroutine_pool[#coroutine_pool+1] = co
-- recv new main function f
f = coroutine_yield "SUSPEND" //接收到f 然后向下运行继续挂起 等待被唤醒,
f(coroutine_yield())
end
end)
else
-- pass the main function f to coroutine, and restore running thread
local running = running_thread
coroutine_resume(co, f) //那么会跑到这里 唤醒co 传入f
running_thread = running
end
return co
end
那么此时将在上图的633行被唤醒 然后执行task 执行完task 回到while true 然后又跑到 f = coroutine_yield “SUSPEND” 给 633行返回参数 true,“SUSPEND”
接下来分析 skynet.sleep
local function suspend_sleep(session, token)
local tag = session_coroutine_tracetag[running_thread]
if tag then c.trace(tag, "sleep", 2) end
session_id_coroutine[session] = running_thread
assert(sleep_session[token] == nil, "token duplicative")
sleep_session[token] = session //加到sleep_session队列中
return coroutine_yield "SUSPEND"
end
function skynet.sleep(ti, token)
local session = c.intcommand("TIMEOUT",ti) //注册超时时间
assert(session)
token = token or coroutine.running()
local succ, ret = suspend_sleep(session, token) //挂起在这里
sleep_session[token] = nil
if succ then
return
end
if ret == "BREAK" then
return "BREAK"
else
error(ret)
end
end
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]
if co == "BREAK" then
session_id_coroutine[session] = nil
elseif co == nil then
unknown_response(session, source, msg, sz)
else
local tag = session_coroutine_tracetag[co]
if tag then c.trace(tag, "resume") end
session_id_coroutine[session] = nil
suspend(co, coroutine_resume(co, true, msg, sz)) //在这里唤醒co 传入参数true, msg, sz
end
else
local p = proto[prototype]
if p == nil then
if prototype == skynet.PTYPE_TRACE then
-- trace next request
trace_source[source] = c.tostring(msg,sz)
elseif session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, prototype)
end
return
end
local f = p.dispatch --是否注册消息处理函数
if f then
local ref = watching_service[source]
if ref then
watching_service[source] = ref + 1
else
watching_service[source] = 1
end
local co = co_create(f)
session_coroutine_id[co] = session
session_coroutine_address[co] = source
local traceflag = p.trace
if traceflag == false then
-- force off
trace_source[source] = nil
session_coroutine_tracetag[co] = false
else
local tag = trace_source[source]
if tag then
trace_source[source] = nil
c.trace(tag, "request")
session_coroutine_tracetag[co] = tag
elseif traceflag then
-- set running_thread for trace
running_thread = co
skynet.trace()
end
end
suspend(co, coroutine_resume(co, session,source, p.unpack(msg,sz))) --解包数据
else
trace_source[source] = nil
if session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, proto[prototype].name)
end
end
end
end
回到挂起的地方
function skynet.sleep(ti, token)
local session = c.intcommand("TIMEOUT",ti)
assert(session)
token = token or coroutine.running()
local succ, ret = suspend_sleep(session, token) //succ,ret = true,nil 执行下面代码然后执行sleep下面的代码后 返回true,SUSPEND
sleep_session[token] = nil
if succ then
return
end
if ret == "BREAK" then
return "BREAK"
else
error(ret)
end
end