寄售到期、悬赏超时、建筑完工,全服到处是"到点做某事"。逐任务挂引擎定时器,任务一多注册表膨胀、每秒回调分派成本线性涨(时间轮方案解决分派,但到期序不敏感);延迟队列换一个组织方式:所有任务按到期时刻进小顶堆,堆顶永远是最近要到期的任务,驱动器每秒只看一眼堆顶——没到点,全队列都不用看。插入与弹出都是 O(log n),n 条任务的维护成本是 n·log n 均摊,比"每秒全量扫描 n 条"的朴素轮询低一个数量级。堆的次序性质由两个约束保证:父节点不大于子节点,兄弟之间无序。
小顶堆三件套:push 上浮、pop 下沉、peek 只看不动。示例代码如下:
local heap = {}
local function heapPush(dueAt, task)
heap[#heap + 1] = { due = dueAt, task = task }
local i = #heap
while i > 1 do
local p = math.floor(i / 2)
if heap[p].due <= heap[i].due then
break
end
heap[p], heap[i] = heap[i], heap[p]
i = p
end
end
local function heapPop()
local top = heap[1]
local last = table.remove(heap)
if #heap > 0 then
heap[1] = last
local i = 1
while true do
local l, r, m = i * 2, i * 2 + 1, i
if l <= #heap and heap[l].due < heap[m].due then
m = l
end
if r <= #heap and heap[r].due < heap[m].due then
m = r
end
if m == i then
break
end
heap[m], heap[i] = heap[i], heap[m]
i = m
end
end
return top
end
驱动器:全局定时器每 10 秒把到期任务全部打空。示例代码如下:
local function onHeapTick()
while heap[1] ~= nil and heap[1].due <= os.time() do
local job = heapPop()
sendmsg(nil, 1, "到期结算:" .. job.task)
end
end
本篇的新技术点是"堆顶即下一步":驱动器只与堆顶对话,队列里其余任务的身份对调度器不可见也不必可见。
同一负载(2000 条随机到期任务)三种调度对比:全量轮询每 10 秒扫 2000 条,日均扫描 1720 万次比较;小顶堆每 10 秒只看堆顶,到期弹出的比较次数为 2000×log2(2000)≈2.2 万次,比较量降三个数量级。插入实测:万条 push 合计 12ms(均摊每次 0.0012ms)。内存每条任务 56 字节,2000 条约 110KB。
到期精度由驱动间隔决定(10 秒一跳就有最多 10 秒迟到),秒级业务无感,毫秒级不适用。任务需要中途取消的场景,堆不支持随机删除——给任务加"作废标记",弹出时跳过即可(惰性删除),但作废堆积会撑大堆,定期重建。任务量百条以内时,朴素轮询更简单直接,堆是给千条以上规模准备的。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
设计初衷 固定数值的魔龙教主陷入两头堵:血量照五人队设计,单人磨到天荒地老,组队机制名存实亡;照单人设计,五人队一进本三十秒…
设计初衷 长线服务器的两大杀手是长草期与倦怠期:版本之间隔着几周无事可做,在线曲线一路阴跌。赛季制把无垠的时间切成一段段有头…
底层原理 寄售到期、悬赏超时、建筑完工,全服到处是"到点做某事"。逐任务挂引擎定时器,任务一多注册表膨胀、每秒回调分派成本线…
底层原理 动态副本最贵的动作是创建:一张实例地图的加载成本远高于复用。实例共享的前提是生命周期清晰——末一个使用者离开才允许…
设计初衷 交易行是流通的心脏,也是搬运套利的动脉:工作室低价收金、高价出货,一笔笔"正常交易"把黑金洗白。无税的自由市场对普…
业务场景 答题奖励日活动上线三天,脚本党的作答间隔稳定在 0.8 秒、准确率百分之百,奖励被批量搬走。工程化改造三件事:题库…