【Effective Modern C++】第三章 转向现代C++:16. 让const成员函数线程安全

const 成员函数承诺 "不修改对象核心逻辑状态",但常因mutable修饰的辅助变量(如多项式根的缓存rootVals、缓存有效标记rootsAreValid),在执行时修改这些变量;

多线程同时调用该 const 函数时,会出现 "不同线程读写同一块内存" 的数据竞争,导致程序行为未定义(即使逻辑上是 "读操作")。

线程安全的解决方案

1. 基础方案:使用std::mutex

  • 适用场景:需要同步多个变量 / 多个操作(如缓存值 + 缓存有效标记),保证操作的原子性;

  • 实现要点:

    • std::mutex声明为mutable(因 const 成员函数中 mutex 被视为 const 对象,而锁 / 解锁操作会修改 mutex 状态);
    • std::lock_guard自动加锁 / 解锁,避免手动解锁遗漏;
  • 副作用:std::mutex不可拷贝 / 移动,导致包含它的类也不可拷贝 / 移动;

  • 示例:多项式根缓存的线程安全实现(加锁后保证缓存计算 / 赋值的原子性)。

2. 轻量方案:使用std::atomic

  • 适用场景:仅需同步单个变量 (如函数调用次数统计),开销比 mutex 小;

  • 实现要点:将统计变量声明为std::atomic<类型>mutable,其操作是原子的(不可分割),避免数据竞争;

  • 副作用:std::atomic同样不可拷贝 / 移动,导致包含它的类不可拷贝 / 移动;

  • 坑点:绝对不能用于多个变量的同步(如缓存值 + 缓存有效标记):

    • 先赋值缓存值、后标记有效:会导致多线程重复计算(违背缓存初衷);
    • 先标记有效、后赋值缓存值:会导致线程读取到未赋值的脏数据。
方案 适用场景 优势 限制
std::mutex 多个变量 / 操作的同步 保证操作原子性 开销略大、类不可拷贝 / 移动
std::atomic 单个变量的同步 开销小、操作原子化 无法同步多个变量 / 操作

例外情况:无需保证线程安全的场景

若能绝对保证 const 成员函数永远不会在多线程环境下调用 (如仅用于单线程独占的类),可省略 mutex/atomic,避免资源开销和类拷贝 / 移动的限制;但这种场景越来越少见,优先保证线程安全。

总结

  • 保证const成员函数的线程安全性,除非可以确信它们不会用在并发语境中。
  • 运用std::atomic类型的变量会比运用互斥量提供更好的性能,但前者仅适用对单个变量或内存区域的操作。

原著在线阅读地址

相关推荐
爱和冰阔落20 分钟前
【Linux】Ctrl+C 到底做了什么?从键盘、kill 到 alarm,一次讲清信号的产生
linux·运维·c++·安卓
水饺编程37 分钟前
第5章,[Win32 章节] :贝塞尔样条曲线(一)
c语言·c++·windows·visual studio
程序员打怪兽1 小时前
C++:类与对象,封装,继承,多态,构造/析构,重载/重写
c++
Escalating_xu2 小时前
【C++ STL简介】从六大组件到容器、迭代器与算法协作
java·c++·算法
Brilliantwxx2 小时前
【Linux】 进程(3)深度解析:从查看进程到进程状态
linux·服务器·网络·c++
啊啊啊啊啊!!!!2 小时前
【c++】二叉搜索树
开发语言·c++
ShineWinsu2 小时前
对于 C++:C++20中Concept(概念) 与 Coroutine(协程)的解析
linux·开发语言·网络·c++·c++20·epoll
liulilittle3 小时前
llmx 学习手册 06 —— CPU 指令集优化(AVX-512 三层演进)
c++·学习·算法·ai·llm
skr爱码士3 小时前
05_Qt 核心模块概览——Qt Core、Gui、Widgets、Quick 的职责划分
c++·qt·系统架构·客户端
郝学胜-神的一滴4 小时前
Horse3D 游戏引擎研发笔记(七):Clydesdale——从流式日志到多输出订阅
c++·qt·unity·游戏引擎·图形渲染·unreal engine·opengl