C++内存序

在 C++ 中,内存序(Memory Order)是多线程编程中原子操作的重要概念,它用于控制原子操作的内存同步行为。C++11 引入了 <atomic> 头文件,提供了内存序来控制多线程环境下的内存访问顺序。

内存序的作用

内存序主要解决两个问题:

  1. 可见性:一个线程对共享数据的修改何时对其他线程可见

  2. 顺序性:操作指令的执行顺序如何被其他线程观察

六种内存序

1. memory_order_relaxed

最宽松的顺序约束,只保证原子性,不保证顺序。

cpp

复制代码
std::atomic<int> x(0);
x.store(1, std::memory_order_relaxed);  // 不保证其他线程立即看到这个值

2. memory_order_consume

依赖于该原子操作的后续操作不会被重排序到该操作之前(依赖关系)。

cpp

复制代码
std::atomic<int*> ptr;
int data;

// 线程1
data = 42;
ptr.store(&data, std::memory_order_consume);

// 线程2
int* p = ptr.load(std::memory_order_consume);
if (p != nullptr) {
    // 保证能看到 data = 42
    int val = *p;
}

3. memory_order_acquire

用于读操作,保证该操作之后的所有读写不会被重排序到该操作之前。

cpp

复制代码
std::atomic<bool> flag(false);
int data = 0;

// 线程1
data = 42;
flag.store(true, std::memory_order_release);

// 线程2
while (!flag.load(std::memory_order_acquire));
// 这里保证能看到 data = 42

4. memory_order_release

用于写操作,保证该操作之前的所有读写不会被重排序到该操作之后。

cpp

复制代码
// 与上面 acquire 配合使用

5. memory_order_acq_rel

同时包含 acquire 和 release 语义,用于读-修改-写操作。

cpp

复制代码
std::atomic<int> counter(0);
counter.fetch_add(1, std::memory_order_acq_rel);

6. memory_order_seq_cst

最严格的顺序约束(默认),保证所有线程看到相同的操作顺序。

cpp

复制代码
std::atomic<int> x(0);
x.store(1);  // 默认使用 memory_order_seq_cst

典型使用模式

1. Release-Acquire 同步

cpp

复制代码
std::atomic<bool> ready(false);
int data = 0;

// 线程1(生产者)
data = 42;
ready.store(true, std::memory_order_release);

// 线程2(消费者)
while (!ready.load(std::memory_order_acquire));
// 这里保证能看到 data = 42

2. Release-Consume 同步

cpp

复制代码
std::atomic<int*> ptr(nullptr);
int value;

// 线程1
value = 100;
ptr.store(&value, std::memory_order_release);

// 线程2
int* p = ptr.load(std::memory_order_consume);
if (p != nullptr) {
    // 保证能看到 p 指向的数据
    int v = *p;  // v = 100
}

3. 自旋锁实现

cpp

复制代码
class SpinLock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire));
    }
    
    void unlock() {
        flag.clear(std::memory_order_release);
    }
};

性能考虑

  • relaxed:性能最好,但需要谨慎使用

  • seq_cst:性能最差,但最容易理解

  • acquire/release:在性能和正确性之间取得平衡

实用建议

  1. 优先使用默认的 seq_cst,除非有性能瓶颈

  2. 理解 happens-before 关系后再使用宽松内存序

  3. 测试多线程代码,内存序错误很难调试

  4. 使用现成的同步原语(如 mutex, condition_variable)通常更安全

示例:无锁计数器

cpp

复制代码
#include <atomic>
#include <thread>
#include <iostream>

class Counter {
    std::atomic<int> count{0};
public:
    void increment() {
        count.fetch_add(1, std::memory_order_relaxed);
    }
    
    int get() const {
        return count.load(std::memory_order_acquire);
    }
};

int main() {
    Counter counter;
    std::thread t1([&]() {
        for (int i = 0; i < 1000000; ++i) {
            counter.increment();
        }
    });
    
    std::thread t2([&]() {
        for (int i = 0; i < 1000000; ++i) {
            counter.increment();
        }
    });
    
    t1.join();
    t2.join();
    
    std::cout << "Count: " << counter.get() << std::endl;
    return 0;
}
相关推荐
汉克老师10 小时前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
智者知已应修善业13 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
云泽80814 小时前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++
AI进化营-智能译站15 小时前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
Morwit15 小时前
QML组件之间的通信方案(暴露子组件)
c++·qt·职场和发展
qeen8715 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
图码16 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
handler0116 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
zhouwy11316 小时前
Linux进程与线程编程详解
linux·c++
A7bert77717 小时前
【YOLOv8pose部署至RDK X5】模型训练→转换bin→Sunrise 5部署
c++·python·深度学习·yolo·目标检测