在前面的幾篇中定時器其實我們已經(jīng)應(yīng)用過了,入口函數(shù)就是靠定時器驅(qū)動的,只不過他的定時為0.參見skynet lua業(yè)務(wù)邏輯的啟動--skynet.start() .
在后面的服務(wù)與服務(wù)交互中,為了保證在A服務(wù)call B服時,B服務(wù)已經(jīng)啟動,我們特意在A服務(wù)call前執(zhí)行了一個無意義的循環(huán).這節(jié)中的定時器就可以解決這個問題,調(diào)用skynet.sleep(xxx)就可以讓協(xié)程掛起.我們看看sleep的代碼:
function skynet.sleep(ti)
local session = c.intcommand("TIMEOUT",ti) --①
assert(session)
local succ, ret = coroutine_yield("SLEEP", session) --②
sleep_session[coroutine.running()] = nil
if succ then
return
end
if ret == "BREAK" then
return "BREAK"
else
error(ret)
end
end
①處會調(diào)用c的timeout接口,前面講過.由于ti不為0,那么他會在c中注冊一個定時器事件,等定時器超時,他會push一個PTYPE_RESPONSE類型,包含session的消息.(c中怎么調(diào)度定時器另起一篇再介紹)
②處導(dǎo)致正在執(zhí)行的協(xié)程掛起,這會讓corutine.resume返回去執(zhí)行suspend.
suspend()函數(shù)中'sleep'命令的處理只是關(guān)聯(lián)了兩個表,一個用co作key,session作value,另一個以session作key,co作value.他們的作用是,當(dāng)收到定時器消息時能夠快速找到關(guān)聯(lián)的協(xié)程co,并恢復(fù)該協(xié)程co,這樣經(jīng)sleep掛起的協(xié)程就又恢復(fù)了.
注意skynet.sleep()參數(shù)是以100為單位,即100表示一秒.
skynet.sleep()應(yīng)該很好理解,再來稍難點的.有這樣一個需求:如何像linux c 一樣創(chuàng)建兩個線程,然后獨自運行線程函數(shù).skynet提供了skynet.fork(),一個例子如下:
skynet.start(function()
function myfork( val )
while true do
print('myfork ', val, ' !!!! ')
skynet.sleep(200)
end
end
local co1 = skynet.fork(myfork, 1)
local co2 = skynet.fork(myfork, 2)
end)
運行結(jié)果為:
可以看到兩個'線程函數(shù)'交替的運行了,我們來分析一下:
function skynet.fork(func,...)
local args = { ... }
local co = co_create(function()
func(tunpack(args))
end)
table.insert(fork_queue, co)
return co
end
可以看到fork實際上是創(chuàng)建了一個協(xié)程函數(shù),并插入到表里,那什么時候開始執(zhí)行協(xié)程函數(shù)呢?
我們追溯fork_queue,發(fā)現(xiàn)他是在skynet.dispatch_message()被執(zhí)行的:
function skynet.dispatch_message(...)
local succ, err = pcall(raw_dispatch_message,...)
while true do
local key,co = next(fork_queue) --表示獲取表的第一個元素和key,t={12,a=34,5,c}都可以訪問到
if co == nil then
break
end
fork_queue[key] = nil
local fork_succ, fork_err = pcall(suspend,co,coroutine.resume(co))
if not fork_succ then
if succ then
succ = false
err = tostring(fork_err)
else
err = tostring(err) .. "\n" .. tostring(fork_err)
end
end
end
assert(succ, tostring(err))
end
我們看到必須要等到skynet.raw_dispatch_message函數(shù)執(zhí)行完成.
結(jié)合前面的幾篇文章分析,必須等到skynet.raw_dispatch_message()里的suspend函數(shù)執(zhí)行完畢.而suspend函數(shù)開始執(zhí)行要等到coroutine.resume()返回.所以必須等到執(zhí)行中的協(xié)程掛起,才有機會執(zhí)行fork里的協(xié)程了.
要想執(zhí)行中的協(xié)程被掛起,除了調(diào)用skynet.sleep()之外,當(dāng)前消息回調(diào)函數(shù)執(zhí)行完畢也會調(diào)用coroutine_yield "EXIT",參看前面講解co_create()的部分.
需要注意的是,在lua里多個協(xié)程函數(shù)不可能像linux c多線程一樣真正同時執(zhí)行,同一時間只有一個協(xié)程函數(shù)在執(zhí)行,只不過利用協(xié)程調(diào)度,可以做到同時執(zhí)行的假象罷了.
所以當(dāng)一個協(xié)程里執(zhí)行死循環(huán)時,另一個協(xié)程時不可能有機會再執(zhí)行的,這點千萬要注意!!!