skynet 是多线程框架,此外在 skynet 中,每个服务对应一个 lua 虚拟机,一个虚拟机上可以跑多个协程,但同一时刻只能有一个协程,每条消息处理由协程来完成。
在 skynet 中,线程创建方式:
skynet.fork(func,…)
一、测试
main.lua
local skynet = require "skynet"
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)
测试结果:
可以看到两个线程函数交替运行。
二、skynet.fork 原理
skynet.lua
function skynet.fork(func,...)
local n = select("#", ...)--获取参数个数
local co
if n == 0 then
co = co_create(func)
else
local args = { ... }
--table.unpack(table, start, end):返回 table 中 [start, end] 区间的元素,start 默认为1,end 默认为最后一个元素的位置
co = co_create(function() func(table.unpack(args,1,n)) end)--创建一个协程,相当于创建一个lua虚拟机
end
tinsert(fork_queue, co)--将协程加到fork_queue队列中
return co
end
从协程池中取出一个空闲协程,没有,则创建一个新的协程:
local function co_create(f)
local co = tremove(coroutine_pool) --从协程池table末尾取出一个协程
--协程池table为空,则创建新协程
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
session_coroutine_id[co] = nil
session_coroutine_address[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(coroutine_yield())
end
end)
else
-- pass the main function f to coroutine, and restore running thread
local running = running_thread
coroutine_resume(co, f)
running_thread = running
end
return co
end
处理fork_queue中的fork协程
function skynet.dispatch_message(...)
local succ, err = pcall(raw_dispatch_message,...)
--处理fork_queue中的fork协程
while true do
local co = tremove(fork_queue,1)
if co == nil then
break
end
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