Apollo 技术文档Apollo 技术文档
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
  • MMORPG 架构 QA

Q59: 如何减少锁竞争?

核心结论

减少锁竞争的核心,不是把锁从代码里删掉,而是减少共享状态和临界区长度。

真正有效的思路通常是:

  • 让更多数据只被一个线程拥有
  • 缩短持锁时间
  • 减少高频全局结构
  • 把竞争从“抢同一把锁”变成“分片或串行归属”

无锁结构只是选项之一,不是默认答案。

一、锁竞争真正会带来什么

常见代价包括:

  • 线程阻塞
  • 上下文切换
  • cache line 抖动
  • 尾延迟变差

所以锁竞争的问题不只是“CPU 高”,还常常表现为系统不稳定、偶发卡顿和吞吐骤降。

二、先找出竞争来自哪里

高竞争锁通常集中在这些位置:

  • 全局 map
  • 消息队列
  • 定时器管理器
  • 日志系统
  • 对象池或连接池

所以第一步不是改并发模型,而是先找出哪些数据结构真的在被争抢。

三、最有效的办法通常是减少共享

1. 单线程拥有

让某类对象始终由固定线程或 Actor 拥有,其他线程通过消息交互。

这通常比给对象加很多锁更稳。

2. 分片

把一把全局锁拆成:

  • 按 key 分片
  • 按房间分片
  • 按玩家或场景分区

这样竞争会明显下降。

3. 读写分离或快照化

某些读多写少结构,更适合用:

  • 只读快照
  • copy-on-write
  • 版本化数据

而不是所有访问都抢同一把 mutex。

四、缩短临界区比换锁类型更重要

很多锁竞争并不是锁本身的问题,而是持锁范围太大。

常见坏味道包括:

  • 持锁期间做 I/O
  • 持锁期间做复杂计算
  • 持锁期间调用其他模块

这会把本来可控的锁变成热点瓶颈。

五、无锁不是银弹

无锁结构能减少一部分阻塞,但也会带来:

  • 代码复杂度上升
  • 调试困难
  • 内存序问题
  • ABA 等并发风险

只有在热点明确、收益明确时才值得上。

六、日志、定时器、队列最容易成为隐形热点

很多系统主逻辑看起来没问题,但公共设施层会变成全局锁黑洞。

例如:

  • 全局日志写锁
  • 单一优先队列定时器
  • 中央对象池

这类组件应该优先考虑:

  • 分线程缓冲
  • 批量合并
  • 分片结构

七、工程上更稳妥的优化顺序

常见做法是:

  1. 先通过 profiling 和 tracing 找热点锁
  2. 先缩小临界区
  3. 再减少共享和做分片
  4. 最后再考虑更激进的无锁结构

这通常比一上来重写成 lock-free 更稳。

八、常见误区

1. 锁竞争高就说明锁不能用

不对。很多时候是共享设计有问题,不是锁这种原语本身有问题。

2. 读写锁一定比互斥锁好

不一定。写多、读临界区短时,读写锁可能更差。

3. 无锁一定性能更高

不对。要看争用模式、数据结构和实现质量。

参考资料

  • 并发 profiling、Actor 化和分片锁实践资料
在 GitHub 上编辑此页
最后更新: 3/20/26, 6:06 AM
贡献者: cuihairu