skynet服務之協程的威力

    接上一篇分析skynet服務之launcher,本篇咱們繼續來分析一下lua中的協程如何與服務有機結合的,關於lua中協程的解釋參見本文lua中協程的理解》; html

上一篇分析到,當一個lua服務收到消息後,在lua層,最終會執行到raw_dispatch_message函數,代碼以下: session

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
            session_id_coroutine[session] = nil
            suspend(co, coroutine_resume(co, true, msg, sz))
        end
    else
        local p = proto[prototype]
        if p == nil then
            if 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
            suspend(co,
coroutine_resume(co, session,source, p.unpack(msg,sz)))
        elseif session ~= 0 then
            c.send(source, skynet.PTYPE_ERROR, session, "")
        else
            unknown_request(session, source, msg, sz, proto[prototype].name)
        end
    end
end
函數

函數先經過f = p.dispatch取到特定服務的消息處理函數,而後調用co_create(f)建立一個協程,該函數實現以下: lua

local coroutine_pool = setmetatable({}, { __mode = "kv" })

local function co_create(f)
    local co = table.remove(coroutine_pool)
    if co == nil then
        co = coroutine.create(function(...)
            f(...)
            while true do
                f = nil
                coroutine_pool[#coroutine_pool+1] = co
                f = coroutine_yield "EXIT"
                f(coroutine_yield())
            end
        end)
    else
        coroutine_resume(co, f)
    end
    return co
end
spa

以上代碼的中使用了弱表來做爲協程池,關於lua中的弱表請參考《lua中的弱表理解》; prototype

上述函數先經過table.remove(coroutine_pool)coroutine_pool表中pop最後一個元素,即一個協程, 協程

若是不存在協程,就會執行coroutine.create(function(...))來建立一個新的協程,協程執行函數接受可變參數, htm

注意:此時並未執行協程函數; blog

若是從協程庫中取到了一個協程,直接調用了coroutine_resume(co, f),這個函數對應函數爲: rem

local coroutine_resume = profile.resume

而profile.resume對應C庫函數lresume(),該函數實現代碼以下:

static int
lresume(lua_State *L) {
    lua_pushvalue(L,1);
    
    return timing_resume(L);
}

接着調用了timing_resume(L),函數實現代碼以下

static int
timing_resume(lua_State *L) {
    lua_pushvalue(L, -1);
    lua_rawget(L, lua_upvalueindex(2));
    if (lua_isnil(L, -1)) {        // check total time
        lua_pop(L,2);    // pop from coroutine
    } else {
        lua_pop(L,1);
        double ti = get_time();
#ifdef DEBUG_LOG
        fprintf(stderr, "PROFILE [%p] resume %lf\n", lua_tothread(L, -1), ti);
#endif
        lua_pushnumber(L, ti);
        lua_rawset(L, lua_upvalueindex(1));    // set start time
    }

    lua_CFunction co_resume = lua_tocfunction(L, lua_upvalueindex(3));

    return co_resume(L);}

相關文章
相關標籤/搜索