当前位置: 首页 > news >正文

C++ 内存序模型(Memory Model)


1. C++ 内存模型概览

C++11 引入了内存模型(memory model),主要解决多线程共享内存访问的可见性与顺序性问题。它定义了:

  1. 程序顺序(program order):单线程内的顺序是固定的。
  2. 多线程访问的顺序(inter-thread ordering):不同线程对共享变量的访问可能乱序。
  3. 原子操作(atomic operations):保证对某个共享对象的操作是不可中断的。
  4. 内存序(memory order):定义原子操作的可见性、同步约束。

在 C++ 中,关键组件是:

  • std::atomic<T>:原子变量模板
  • std::memory_order:原子操作的内存序
  • std::mutexstd::condition_variable:高级同步原语(内部依赖内存序)

2. 原子操作(Atomic Operations)

std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_seq_cst); // 原子自增

特点:

  • 不可分割(atomic)
  • 防止数据竞争(data race)
  • 配合内存序可以控制可见性和顺序

注意:非原子变量在多线程写入时,如果没有同步机制,将会产生数据竞争,这是未定义行为


3. C++ 内存序类型

C++ 定义了 6 种内存序,按严格性从强到弱排列:

内存序含义顺序约束典型用途
memory_order_seq_cst顺序一致最严格,所有线程对同一原子操作的观察一致默认选择,简单、可靠
memory_order_acquire获取对当前线程之后的读写不会被重排到 acquire 之前读锁、标志位同步
memory_order_release释放对当前线程之前的读写不会被重排到 release 之后写锁、发布数据
memory_order_acq_rel获取-释放combine acquire + release读写锁
memory_order_relaxed放宽不保证跨线程顺序,仅保证原子性高性能计数器、统计
memory_order_consume数据依赖减少 acquire 的开销,但依赖支持有限特殊优化

4. 内存序示意

考虑两个线程共享变量 xy

std::atomic<int> x{0}, y{0};
int r1, r2;Thread 1:
x.store(1, std::memory_order_release);
r1 = y.load(std::memory_order_acquire);Thread 2:
y.store(1, std::memory_order_release);
r2 = x.load(std::memory_order_acquire);

解释:

  • store(..., release) 确保发布之前的写操作对其他线程可见
  • load(..., acquire) 确保获取之后的读写不会提前
  • 如果只用 relaxed,可能出现 r1==0 && r2==0,即重排序导致线程看不到对方的写

5. 内存序规则总结

  1. Release-Acquire:确保线程间数据同步
  2. Relaxed:只保证原子性,不保证顺序
  3. Seq_Cst:全局单一顺序,保证最强一致性
  4. 依赖链:Acquire-Release 可通过数据依赖传播可见性

6. 内存屏障(Memory Fences)

C++ 提供了 atomic_thread_fence,用于手动插入内存屏障:

std::atomic_thread_fence(std::memory_order_acquire);
std::atomic_thread_fence(std::memory_order_release);
std::atomic_thread_fence(std::memory_order_seq_cst);

作用:

  • 阻止编译器和 CPU 对原子操作前后的普通内存访问进行乱序
  • 常用于 lock-free 数据结构(队列、环形缓冲区)同步

7. 内存序与 CPU 架构

不同 CPU 架构对内存序的支持不同:

CPU默认顺序特殊说明
x86Total Store Order (TSO)store-store 不乱序,load-store 可乱序
ARMWeakly Orderedload-store 可能乱序,需要 fence
POWERVery Weak需要 fence 实现 acquire/release

C++ 的内存序会在编译器层面生成对应的 CPU 指令(mfencedmbsync 等)。


8. 常用模式示例

8.1 Release-Acquire 发布标志位

std::atomic<bool> ready{false};
int data = 0;void producer() {data = 42;ready.store(true, std::memory_order_release);
}void consumer() {while(!ready.load(std::memory_order_acquire));// 此时保证 data 可见std::cout << data << std::endl; // 42
}

8.2 Relaxed 计数器

std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed); // 高性能计数

8.3 Seq_Cst 全局顺序保证

std::atomic<int> x{0};
x.store(1); // 默认 seq_cst
int r = x.load(); // 保证所有线程看到一致顺序

9. 小结与实践建议

  1. 默认选择 seq_cst:简单可靠,避免错综复杂的同步 bug
  2. 性能优化时使用 relaxed:只用于不依赖顺序的统计计数、采样
  3. Release-Acquire 是锁自由数据结构的核心
  4. 避免直接用非原子共享变量多线程写入
  5. 结合 mutex/condition_variable 更简单:C++ 内存序复杂,很多场景 mutex 性能和正确性都更优

10.应用示例


示例 1:锁自由单生产者单消费者队列(SPSC Queue)

特点:生产者写入,消费者读取,要求高性能,使用 Release-Acquire 保证同步。

#include <atomic>
#include <vector>
#include <iostream>template <typename T>
class SPSCQueue {std::vector<T> buffer;const size_t size;std::atomic<size_t> head{0};std::atomic<size_t> tail{0};public:SPSCQueue(size_t sz) : size(sz), buffer(sz) {}bool push(const T& value) {size_t h = head.load(std::memory_order_relaxed);size_t next = (h + 1) % size;if (next == tail.load(std::memory_order_acquire)) // 空间检查return false;buffer[h] = value;head.store(next, std::memory_order_release); // 发布数据return true;}bool pop(T& value) {size_t t = tail.load(std::memory_order_relaxed);if (t == head.load(std::memory_order_acquire)) // 队列空return false;value = buffer[t];tail.store((t + 1) % size, std::memory_order_release); // 更新 tailreturn true;}
};

说明:

  • head 发布写入,消费者获取时 acquire 保证看到 buffer 中的数据
  • 无需锁,性能高,适合实时 SLAM 数据管线

示例 2:跨线程标志位同步

场景:生产者准备数据,消费者等待数据可用。使用 Release-Acquire 内存序保证数据可见。

#include <atomic>
#include <thread>
#include <iostream>std::atomic<bool> ready{false};
int shared_data = 0;void producer() {shared_data = 100; // 写入数据ready.store(true, std::memory_order_release); // 发布标志
}void consumer() {while(!ready.load(std::memory_order_acquire)) {} // 获取标志std::cout << "Data = " << shared_data << std::endl; // 确保读到正确数据
}int main() {std::thread t1(producer);std::thread t2(consumer);t1.join(); t2.join();
}

说明:

  • 如果使用 relaxed,消费者可能看到 ready=trueshared_data 尚未更新
  • releaseacquire 形成同步,确保跨线程数据顺序正确

示例 3:高性能统计计数器(Relaxed 内存序)

场景:多线程累加统计量,不依赖精确顺序,仅保证原子性。

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>std::atomic<int> counter{0};void worker(int n) {for (int i = 0; i < n; i++) {counter.fetch_add(1, std::memory_order_relaxed); // 高性能累加}
}int main() {const int N = 1000000;std::thread t1(worker, N);std::thread t2(worker, N);t1.join(); t2.join();std::cout << "Total count = " << counter.load() << std::endl;
}

说明:

  • 使用 relaxed 可以避免不必要的 fence 指令,提高性能
  • 只要最终累加结果一致即可,顺序无关紧要

总结

示例类型内存序说明
1SPSC Queuerelease/acquire锁自由队列,保证生产者写入数据对消费者可见
2跨线程标志位release/acquire发布数据+等待标志同步
3高性能计数器relaxed高性能累加,不依赖顺序

http://www.dtcms.com/a/574977.html

相关文章:

  • 网站制作程序网站设计公司市场容量
  • 自考网页制作与网站建设什么设计师最吃香
  • 怎么做淘宝客网站和APPwordpress不升级
  • 公司网站域名价格什么叫做网络营销
  • 惠州网站制作询问薇wordpress非常卡
  • 中小企业网站功能模块及数据库表线上少儿编程网站开发
  • 两万字!JVM虚拟机笔记
  • PostIn零基础学习 - 如何快速导入swagger、OpenApi数据,实现数据迁移
  • ref创建对象类型的响应式数据
  • wordpress 木马 查南宁市优化网站
  • 网站维护 上海介绍自己做的网站
  • 10.进程间通信(五)
  • 剪贴板监控记:用 Go 写一个 Windows 剪贴板监控器
  • 建设网站套餐ui平面设计
  • anchor-based与anchor-free对比
  • 8B/10B编码技术深度解析
  • 给六人游做网站开发的企业营销网站建设的基本步骤
  • 网站设计的发展趋势义乌网站设计制作价格
  • 国外网站建设模板河南省水利建设管理处网站
  • 开封河南网站建设以下哪个不属于网络营销的特点
  • 数据挖掘:python招聘数据分析预测系统 招聘数据平台 +爬虫+可视化 +django框架+vue框架 大数据技术✅
  • 天津河西做网站公司学校网站做网页飘窗怎么做
  • 【Algorithm】Day-11
  • dw做的网站有什么缺陷四川平台网站建设哪里有
  • 张琦加盟 2025 创始人 IP+AI 万人大会:AI 时代,IP 破局增长的实战方法都在这
  • 南京建设网站的公司dw网页制作成品12页
  • 手机轻松控制电脑:局域网内远程操控B站/抖音实战教程
  • 做网站要求高吗最新wordpress模板
  • 企业网站现状wordpress 安全狗
  • Spring Cloud 总览:微服务的生态基石