std::atomic变量必须用成员函数(如load()、store()、fetch_add())访问,普通赋值或自增会破坏原子性;++counter等重载操作仅对整型和指针安全,浮点型不支持。

std::atomic 不能只改声明,还要改读写方式
声明 std::atomic<int></int> 只是第一步。如果仍用普通赋值、自增或指针解引用去访问它,编译器可能绕过原子语义,导致 CPU 缓存不一致 —— 比如一个线程看到旧值,另一个看到新值,且这种不一致不会报错,只在结果上偶然暴露。
- 错误写法:
counter = counter + 1;或int x = counter;:即使counter是std::atomic<int></int>,这种写法不保证原子性,底层可能拆成 load-modify-store 三步,中间被其他线程打断 - 正确写法:必须用成员函数,如
counter.fetch_add(1)、counter.load()、counter.store(42) - 特例:
++counter和counter++对std::atomic是重载过的,可安全使用,但仅限于整型和指针类型;浮点型不支持
std::mutex 无法解决内存可见性,得配 memory_order
加锁能防竞态,但不自动保证“锁释放后,修改对其他线程立即可见”。尤其在弱一致性架构(如 ARM、RISC-V)或高优化等级(-O3)下,编译器/CPU 可能重排指令,让其他线程看到未刷新的缓存副本。
- 典型现象:线程 A 修改了某个标志位并解锁,线程 B 加锁后读到旧值,甚至永远读不到更新
- 根本原因:
std::mutex::unlock()默认是memory_order_release,lock()默认是memory_order_acquire,但仅对锁保护的临界区有效;若你在锁外读写共享变量,就完全不受保护 - 补救办法:对非临界区的跨线程通信变量,显式用
std::atomic并指定memory_order_acquire/memory_order_release,而不是依赖 mutex 的副作用
std::shared_mutex 在读多写少时反而加剧缓存抖动
std::shared_mutex 允许多个读者并发,但它的内部实现通常依赖原子计数器和条件变量,在高并发读场景下,频繁的 cache line 争用会导致伪共享(false sharing),性能可能比单个 std::mutex 还差。
- 容易踩的坑:把所有只读操作都套上
shared_lock,却没意识到每次 lock/unlock 都要触达同一块 cache line - 验证方法:用
perf stat -e cache-misses,cache-references对比两种锁的缓存失效次数 - 更轻量的替代:如果只是发布-订阅类通知(比如配置变更),用
std::atomic<bool></bool>+ 自旋 +memory_order_acquire读,比任何 mutex 更快也更确定
std::barrier 不是万能同步点,它不传播内存修改
std::barrier 能让 N 个线程在某点齐步走,但它本身不带内存屏障语义。线程 A 在 barrier 前写入的数据,线程 B 在 barrier 后读取时,仍可能因 CPU 缓存未同步而读到陈旧值。
立即学习“C++免费学习笔记(深入)”;
- 常见误用:在 barrier 前更新全局状态,以为 barrier 后所有线程自然“看到最新值”
- 正确做法:barrier 前写操作用
store(..., memory_order_release),barrier 后读操作用load(..., memory_order_acquire);或者直接把共享数据封装进std::atomic并全程用原子操作 - 注意:C++20 的
std::barrier构造时不接受 memory order 参数,这点和std::latch不同,不能靠它隐式同步


















