skynet源码 --- queue.lua
在使用skynet的时候,同一个service,在处理业务时可能出现这种需求:
1 逻辑A首先在处理,在调用call的时候挂起了当前协程,这时候service收到了退出的消息,需要执行落地操作,这时候A是没有处理完的,这样的结果并不是我们想要的,我们想要的顺序应该是A执行完后,才会执行落地操作并且退出service。
2 当service的A逻辑与B逻辑都有修改到公共的upvalue时,先执行A逻辑,A逻辑获取到公共的upvalue判断是佛满足后,执行了call操作挂起了当前协程,如果这时候要执行B逻辑,B逻辑获取并修改了公共upvalue,这时候A逻辑等待call返回后,继续获取upvalue值进行操作,可能会出现错误,这时候的理想流程也应当是两个逻辑有使用到公共数据时,应该等待先执行的逻辑执行完才执行后来者。
3 与2的情况很像,但是更加严重,A逻辑与B逻辑要获取锁的话,可能会出现死锁的情况。
针对上述问题,必须有一个排队机制,保证先到先执行,执行完成后才继续执行下一个消息。
1 local traceback = debug.traceback 2 local table = table 3 4 function skynet.queue() 5 local current_thread 6 local ref = 0 7 local thread_queue = {} 8 9 local function xpcall_ret(ok, ...) 10 ref = ref - 1 11 if ref == 0 then 12 current_thread = table.remove(thread_queue,1) 13 if current_thread then 14 skynet.wakeup(current_thread) 15 end 16 end 17 assert(ok, (...)) 18 return ... 19 end 20 21 return function(f, ...) 22 local thread = coroutine.running() 23 if current_thread and current_thread ~= thread then 24 table.insert(thread_queue, thread) 25 skynet.wait() 26 assert(ref == 0) -- current_thread == thread 27 end 28 current_thread = thread 29 30 ref = ref + 1 31 return xpcall_ret(xpcall(f, traceback, ...)) 32 end 33 end 34 35 return skynet.queue
skyent中实现了queue.lua来保证同一个service中的不同协程的执行顺序。
使用方式如下,可以直接在service启动时的回调方法里将所有的command入队:
1 function server.on_command(source, cmd, uid, ...) 2 local fn = command[cmd] 3 if fn then 4 if (cmd == "login") then 5 return fn(source, uid, ...) 6 end 7 -- 执行服务指令 8 local u = onlines[uid] 9 if u then 10 local umeta = u.umeta 11 local queue = u.queue 12 return queue(fn, source, umeta, ...) 13 else 14 ERROR("agent.command[%s] : user[%s] not exists!!!", cmd, uid) 15 end 16 else 17 ... 18 end 19 end
浙公网安备 33010602011771号