CriticalSection 临界区保护

同一个skynet服务中的一条消息处理中,如果调用了一个阻塞api,那么它会被挂起,挂起过程中,这个服务可以响应其他消息,这很可能造成时序问题,要非常小心处理,


换句话说,一旦你的消息处理过程有外部请求,那么闲到的消息未必比厚道的消息先处理完,且每个阻塞调用之后,服务的内部状态都未必和调用前的一致(因为别的消息处理过程可能改变状态).


skynet.queue模块可以帮助你回避这些伪并发引起的复杂性.

local queue = require "skynet.queue"

这样获得的 queue 是一个函数,每次调用它都可以得到一个新的临界区。临界区可以保护一段代码不被同时运行。

local cs = queue()  -- cs 是一个执行队列

local CMD = {}

function CMD.foobar()
  cs(func1)  -- push func1 into critical section
end

function CMD.foo()
  cs(func2)  -- push func2 into critical section
end

比如你实现了这样一个消息分发器,支持 foobar 和 foo 两类消息。如果你使用 cs 这个 skynet.queue 创建出来的队列。那么在上面的处理流程中, func1 和 func2 这两个函数,都不会在执行过程中相互被打断。

如果你的服务收到多条 foobar 或 foo 消息,一定是处理完一条后,才处理下一条,即使 func1 或 func2 中有 skynet.call 这类的阻塞调用。一旦它们被挂起,新的消息到来后,新的处理流程会被排到 cs 队列尾,等待前面的流程执行完毕才会开始。

注:在 func1 函数内部再调用 cs 是合法的。即:

local function func2()
  -- step 3
end

local function func1()
  -- step 2
  cs(func2)
  -- step 4
end

function CMD.foobar()
  -- step 1
  cs(func1)  -- push func1 into critical section
  -- step 5
end

如果你这样写,每次收到 foobar 消息后,程序流程会按 step 1, step 2, step 3, step 4, step 5 这样执行,而不会死锁。

在这个过程中,如果 foobar 消息的处理流程被挂起,即使新的 foobar 消息到来,那么,新的消息会立刻执行 step 1 (因为没有被 cs 保护),然后等前一次的 step 4 结束后(step 5 不在 cs 保护中),开始新的 step 2 。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值