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);}