[知识点] 内存顺序属性的用途和行为

C++标准库中定义了以下几种内存顺序属性:

  1. std::memory_order_relaxed
  2. std::memory_order_consume
  3. std::memory_order_acquire
  4. std::memory_order_release
  5. std::memory_order_acq_rel
  6. std::memory_order_seq_cst

1. std::memory_order_relaxed

  • 定义:不提供同步或顺序保证,只保证原子操作本身的原子性。
  • 用途:适用于不需要同步的情况下,用于计数器等场景。
  • 行为:操作之间可以自由重排序,其他线程可能看到不同的操作顺序。
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 计数器,用于多线程递增
std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; ++i) {
        // 使用 memory_order_relaxed 进行原子加操作
        // 不保证任何顺序,只保证原子性
        counter.fetch_add(1, std::memory_order_relaxed);
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    // 输出最终的计数器值
    std::cout << "Counter: " << counter.load(std::memory_order_relaxed) << std::endl;
    return 0;
}

2. std::memory_order_consume

  • 定义:专用于指针依赖的消费操作,确保后续依赖操作的顺序。
  • 用途 :用于消费操作,确保依赖指针的操作不会被重排序到消费操作之前
  • 行为 :类似于 memory_order_acquire,但仅保证指针依赖的顺序。
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 原子指针,指向整数数据
std::atomic<int*> p;
int data;

void producer() {
    data = 42;
    // 使用 memory_order_release 存储指针
    // 确保 data 的写入在存储指针之前完成
    p.store(&data, std::memory_order_release);
}

void consumer() {
    int* ptr;
    // 使用 memory_order_consume 加载指针
    // 确保指针依赖的操作不会被重排序
    while (!(ptr = p.load(std::memory_order_consume)));
    // 输出指针指向的数据
    std::cout << "Data: " << *ptr << std::endl;
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);

    t1.join();
    t2.join();

    return 0;
}

3. std::memory_order_acquire

  • 定义 :获取操作,确保后续读写操作 不会被重排序到获取操作之前
  • 用途 :用于加载操作,以确保加载后的操作看到的是最新的数据
  • 行为 :获取操作之前的读写操作可能被重排序,但之后的操作不会
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 标志变量,用于同步
std::atomic<int> flag(0);
int data = 0;

void writer() {
    data = 42;
    // 使用 memory_order_release 存储标志
    // 确保 data 的写入在存储标志之前完成
    flag.store(1, std::memory_order_release);
}

void reader() {
    // 使用 memory_order_acquire 加载标志
    // 确保标志被加载后,读取 data 的操作不会被重排序
    while (flag.load(std::memory_order_acquire) != 1);
    // 输出 data 的值
    std::cout << "Data: " << data << std::endl;
}

int main() {
    std::thread t1(writer);
    std::thread t2(reader);

    t1.join();
    t2.join();

    return 0;
}

4. std::memory_order_release

  • 定义:释放操作,确保之前的读写操作不会被重排序到释放操作之后。
  • 用途:用于存储操作,以确保存储前的所有操作完成。
  • 行为:释放操作之后的读写操作可能被重排序,但之前的操作不会。
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 标志变量,用于同步
std::atomic<int> flag(0);
int data = 0;

void writer() {
    data = 42;
    // 使用 memory_order_release 存储标志
    // 确保 data 的写入在存储标志之前完成
    flag.store(1, std::memory_order_release);
}

void reader() {
    // 使用 memory_order_acquire 加载标志
    // 确保标志被加载后,读取 data 的操作不会被重排序
    while (flag.load(std::memory_order_acquire) != 1);
    // 输出 data 的值
    std::cout << "Data: " << data << std::endl;
}

int main() {
    std::thread t1(writer);
    std::thread t2(reader);

    t1.join();
    t2.join();

    return 0;
}

5. std::memory_order_acq_rel

  • 定义:获取和释放操作的组合,适用于读-修改-写操作。
  • 用途 :用于原子操作,如 compare_exchange,以确保操作的前后都不会被重排序。
  • 行为:操作之前和之后的读写操作都不会被重排序。
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 原子计数器,用于多线程递增
std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; ++i) {
        int expected = counter.load(std::memory_order_relaxed);
        // 使用 memory_order_acq_rel 进行比较并交换操作
        // 确保操作之前和之后的读写顺序
        while (!counter.compare_exchange_weak(expected, expected + 1, std::memory_order_acq_rel));
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    // 输出最终的计数器值
    std::cout << "Counter: " << counter.load(std::memory_order_relaxed) << std::endl;
    return 0;
}

6. std::memory_order_seq_cst

  • 定义:获取和释放操作的组合,适用于读-修改-写操作。
  • 用途 :用于原子操作,如 compare_exchange,以确保操作的前后都不会被重排序。
  • 行为:操作之前和之后的读写操作都不会被重排序。
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

// 原子变量
std::atomic<int> x(0), y(0);
int r1, r2;

void thread1() {
    // 使用 memory_order_seq_cst 存储 x
    // 确保全局顺序一致
    x.store(1, std::memory_order_seq_cst);
    // 使用 memory_order_seq_cst 加载 y
    // 确保全局顺序一致
    r1 = y.load(std::memory_order_seq_cst);
}

void thread2() {
    // 使用 memory_order_seq_cst 存储 y
    // 确保全局顺序一致
    y.store(1, std::memory_order_seq_cst);
    // 使用 memory_order_seq_cst 加载 x
    // 确保全局顺序一致
    r2 = x.load(std::memory_order_seq_cst);
}

int main() {
    std::thread t1(thread1);
    std::thread t2(thread2);

    t1.join();
    t2.join();

    // 输出结果
    std::cout << "r1: " << r1 << ", r2: " << r2 << std::endl;
    return 0;
}

总结

  • std::memory_order_relaxed: 最弱的同步和顺序保证,仅保证原子操作本身的原子性。
  • std::memory_order_consume: 专用于指针依赖,确保后续依赖操作的顺序。
  • std::memory_order_acquire: 获取操作,确保后续读写操作不会被重排序到获取操作之前。
  • std::memory_order_release: 释放操作,确保之前的读写操作不会被重排序到释放操作之后。
  • std::memory_order_acq_rel: 获取和释放操作的组合,适用于读-修改-写操作。
  • std::memory_order_seq_cst: 顺序一致性操作,提供最强的同步和顺序保证。
相关推荐
_Coin_-4 分钟前
算法训练营DAY29 第八章 贪心算法 part02
算法·贪心算法
阿维同学14 分钟前
自动驾驶关键算法深度研究
人工智能·算法·自动驾驶
卷到起飞的数分15 分钟前
Java零基础笔记07(Java编程核心:面向对象编程 {类,static关键字})
java·开发语言·笔记
YOLO大师25 分钟前
华为OD机试 2025B卷 - 小明减肥(C++&Python&JAVA&JS&C语言)
c++·python·华为od·华为od机试·华为od2025b卷·华为机试2025b卷·华为od机试2025b卷
谁他个天昏地暗26 分钟前
Java 实现 Excel 文件对比与数据填充
java·开发语言·excel
kaikaile199542 分钟前
使用Python进行数据可视化的初学者指南
开发语言·python·信息可视化
大P哥阿豪43 分钟前
Go defer(二):从汇编的角度理解延迟调用的实现
开发语言·汇编·后端·golang
今天背单词了吗9801 小时前
算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·学习·算法·排序算法·冒泡排序
意疏1 小时前
【Python篇】PyCharm 安装与基础配置指南
开发语言·python·pycharm
看到我,请让我去学习2 小时前
OpenCV编程- (图像基础处理:噪声、滤波、直方图与边缘检测)
c语言·c++·人工智能·opencv·计算机视觉