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

Q79: 如何设计线程安全的容器?

核心结论

线程安全容器的关键,不是给标准容器外面包一把锁,而是先明确:

  • 有多少线程会读
  • 有多少线程会写
  • 是否需要迭代
  • 是否需要顺序保证

不同访问模型下,最佳设计差别很大。没有一种“线程安全容器模板”能覆盖所有场景。

一、先问容器到底要承受什么访问模式

最常见的几种模式包括:

  • 单写多读
  • 多写单读
  • 多写多读
  • 读远多于写

还要进一步问:

  • 是否需要按 key 查
  • 是否需要顺序遍历
  • 是否允许快照读

这些都会直接影响设计。

二、粗粒度锁不是原罪

很多场景里,一个简单 mutex 包住整个容器其实已经够用:

  • 逻辑简单
  • 容量不大
  • 并发不高

不要一上来就追求分段锁或无锁结构。过度设计会增加很多复杂度。

三、只有在热点明确时才值得细化

如果 profiling 证明容器锁确实是热点,再考虑:

  • 分段锁
  • 读写锁
  • copy-on-write
  • 无锁结构

这时重点应围绕真实瓶颈,而不是概念先进。

四、线程安全不只是一把锁的问题

很多容器真正难的地方在:

  • 迭代时结构变化
  • 元素生命周期
  • 返回引用或指针后的有效性

也就是说,即使增删查改都“加锁了”,API 设计仍可能不安全。

五、API 设计经常比内部实现更关键

更稳妥的容器接口通常会避免:

  • 直接暴露内部引用
  • 持锁外返回可失效迭代器
  • 让调用方不清楚所有权

线程安全容器的价值,不只是内部没 data race,还要让调用方不容易误用。

六、读多写少场景可以考虑快照化

例如配置、路由表、只读索引等,很多时候更适合:

  • 不可变快照
  • copy-on-write
  • 版本切换

这样读路径几乎不用加锁,通常比一把大读写锁更稳。

七、工程上更稳妥的设计顺序

常见做法是:

  1. 先明确访问模式
  2. 先用最简单可行方案
  3. 有热点证据后再细化锁粒度
  4. API 设计优先保证生命周期安全

这样实现和维护成本更可控。

八、常见误区

1. 线程安全容器一定要无锁

不对。很多场景简单锁方案就已经足够好。

2. 只要内部加锁,API 就安全

不一定。返回引用、迭代器和生命周期问题一样会出错。

3. 读写锁一定更适合读多写少

很多实际场景里,读写锁的开销和争用并不一定划算。

参考资料

  • 并发容器、分段锁和快照读实践资料
在 GitHub 上编辑此页
最后更新: 3/20/26, 6:06 AM
贡献者: cuihairu