skynet 定時器sleep與fork

在前面的幾篇中定時器其實我們已經(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.他們的作用是,當收到定時器消息時能夠快速找到關(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é)果為:

1fa40c805a061e1d82ab13bbbd545f3a_70.png

可以看到兩個'線程函數(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()之外,當前消息回調(diào)函數(shù)執(zhí)行完畢也會調(diào)用coroutine_yield "EXIT",參看前面講解co_create()的部分.

需要注意的是,在lua里多個協(xié)程函數(shù)不可能像linux c多線程一樣真正同時執(zhí)行,同一時間只有一個協(xié)程函數(shù)在執(zhí)行,只不過利用協(xié)程調(diào)度,可以做到同時執(zhí)行的假象罷了.

所以當一個協(xié)程里執(zhí)行死循環(huán)時,另一個協(xié)程時不可能有機會再執(zhí)行的,這點千萬要注意!!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容