首页 > 系统相关 >详解 C++ 的内存序模型

详解 C++ 的内存序模型

时间:2024-11-19 11:14:43浏览次数:3  
标签:std C++ 详解 线程 内存 memory 操作 order

详解 C++ 的内存序模型

C++ 提供了内存序模型来控制多线程程序中不同线程对共享内存的访问顺序。最常用的是顺序一致性内存模型(memory_order_seq_cst,但它也提供了其他模型(如 memory_order_relaxed)以优化性能。


一、顺序一致性内存模型(memory_order_seq_cst

定义

顺序一致性内存模型保证多线程程序中的所有内存操作以全局一致的顺序执行。这种模型使程序的行为更接近单线程程序的直觉。

特点
  1. 全局操作顺序

    • 所有线程中的所有原子操作都按照某个全局一致的顺序执行。
    • 不同线程观察到的原子操作顺序相同。
  2. 同步可预测

    • 线程对共享数据的操作结果可预测,符合程序员的直觉。
  3. 开销

    • 是最严格的内存模型,硬件需要更多的开销(如 CPU 指令重排序限制和内存屏障)。
示例代码
#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> a{0};
std::atomic<int> b{0};

void thread1() {
    a.store(1, std::memory_order_seq_cst); // 顺序一致地存储
    std::cout << "Thread 1: " << b.load(std::memory_order_seq_cst) << std::endl; // 顺序一致地加载
}

void thread2() {
    b.store(1, std::memory_order_seq_cst);
    std::cout << "Thread 2: " << a.load(std::memory_order_seq_cst) << std::endl;
}

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

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

    return 0;
}
输出行为

无论硬件如何优化,由于使用了 memory_order_seq_cst,两个线程的操作将按照全局一致的顺序执行。例如:

Thread 1: 0
Thread 2: 1

二、其他内存序

1. memory_order_relaxed
定义
  • 最弱的内存序模型,不保证操作的顺序
  • 只保证当前操作的原子性。
特点
  1. 无同步

    • 不提供线程间的同步,操作可能被重新排序。
    • 不适合用于需要线程间通信的场景。
  2. 高性能

    • 性能最好,因为没有内存屏障和指令重排序限制。
示例代码
#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> x{0};
std::atomic<int> y{0};
int r1 = 0, r2 = 0;

void thread1() {
    x.store(1, std::memory_order_relaxed);
    r1 = y.load(std::memory_order_relaxed);
}

void thread2() {
    y.store(1, std::memory_order_relaxed);
    r2 = x.load(std::memory_order_relaxed);
}

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

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

    std::cout << "r1: " << r1 << ", r2: " << r2 << std::endl;
    return 0;
}
输出行为
  • memory_order_relaxed 不保证操作顺序,可能输出 r1 = 0, r2 = 0,即两个线程可能完全看不到对方的操作。
适用场景
  • 无依赖的原子计数器(如多线程中的计数统计),不需要线程间通信。

2. memory_order_acquirememory_order_release
定义
  • memory_order_acquire:防止之前的操作被重排序到当前操作之后。
  • memory_order_release:防止之后的操作被重排序到当前操作之前。
特点
  1. 配对使用

    • 通常需要一个线程使用 memory_order_release 存储,另一个线程使用 memory_order_acquire 加载。
    • 能实现轻量级的线程同步。
  2. 不保证全局顺序

    • 仅适用于特定线程间的同步。
示例代码
#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> data{0};
std::atomic<bool> flag{false};

void thread1() {
    data.store(42, std::memory_order_relaxed); // 存储数据
    flag.store(true, std::memory_order_release); // 发布数据
}

void thread2() {
    while (!flag.load(std::memory_order_acquire)); // 等待标志
    std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl; // 加载数据
}

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

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

    return 0;
}
输出行为

输出始终是:

Data: 42

线程 1 发布数据后,线程 2 确保读取到的数据是线程 1 存储的。

适用场景
  • 用于生产者-消费者模式的轻量级同步。

3. memory_order_consume
定义
  • memory_order_acquire 更弱,仅禁止与数据依赖相关的重排序。
特点
  • 几乎未被广泛实现,很多编译器直接将其等同于 memory_order_acquire
  • 一般不建议使用。

4. memory_order_acq_rel
定义
  • 结合了 memory_order_acquirememory_order_release 的特点。
  • 同时保证“当前操作之前的代码不会被重排序到当前操作之后”和“当前操作之后的代码不会被重排序到当前操作之前”。
适用场景
  • 在同一个操作中即需要加载又需要存储时(如比较交换 compare_exchange)。

三、性能对比

内存序类型内存屏障强度性能使用场景
memory_order_relaxed无内存屏障,允许重排序性能最好无线程间同步依赖的场景
memory_order_acquire防止之前的操作被重排序到之后较高性能线程间轻量级同步
memory_order_release防止之后的操作被重排序到之前较高性能线程间轻量级同步
memory_order_acq_rel同时限制之前和之后的重排序较低性能加载和存储同时需要同步的场景
memory_order_seq_cst最严格,完全禁止重排序性能最低全局一致性顺序的多线程同步场景

总结

  1. memory_order_seq_cst 是最安全但性能最低的内存序模型,适用于需要全局一致性的多线程程序。
  2. 其他内存序(如 memory_order_relaxed)允许更高性能,但需要程序员清楚理解线程间的同步关系,合理使用它们。
  3. 在实际开发中,尽量根据需求选择合适的内存序模型,不要一味追求性能而牺牲代码的正确性。

标签:std,C++,详解,线程,内存,memory,操作,order
From: https://blog.csdn.net/qq_43689451/article/details/143844859

相关文章

  • Python 基于C++ & python的键盘记录器发送指定邮箱
    Python基于C++&python的键盘记录器发送指定邮箱1.简介:采用c++与python语言相结合的方法,c++负责采集键盘操作记录到文本,python脚本实时将文本内容发送至指定邮箱。资源文件已打包,可设置开机自启动。2.kb.cpp键盘记录实现代码:#include<iostream>#include<stdio.h>......
  • 实现简易计算器 网格布局 QT环境 纯代码C++实现
    问题:通过代码完成一个10以内加减法计算器。不需要自适应,界面固定360*350。"="按钮90*140,其它按钮90*70。参考样式#defineDEFULT_BUTTON_STYLE"\QPushButton{\color:#000000;\border:1pxsolid#AAAAAA;\border-radius:0;\background-color:#FFFFFF;......
  • 循环内的会被其他核修改的变量需要使用volatile的例子说明,及内存屏障的原理及使用
    一、背景之前在做 rt-linux之防止优先级反转-CSDN博客 里的优先级反转的实验的验证时,在模拟长时间占锁的代码里使用了死循环死等一个标志位的方式,遇到了这篇博客里说的这个不加volatile导致的代码运行与编写预期不一致的情况。我觉得是一个比较典型的情况,所以有必要单独写一......
  • 【网络安全】-网络安全的分类详解_网络安全类型
    介绍网络安全是保护计算机系统、网络和数据免受未经授权的访问、攻击、破坏或泄露的实践。对于初学者来说,了解网络安全的分类是建立安全意识的关键。在本教程中,我们将深入研究网络安全的不同方面,从基础理论到实际操作,以帮助小白用户更好地保护自己的数字生活。1.网络层......
  • 基于 Levenberg - Marquardt 法的 BP 网络学习改进算法详解
    基于Levenberg-Marquardt法的BP网络学习改进算法详解一、引言BP(BackPropagation)神经网络在众多领域有着广泛应用,但传统BP算法存在收敛速度慢、易陷入局部最优等问题。Levenberg-Marquardt(LM)算法作为一种有效的优化算法,被应用于改进BP网络学习,能够显著提高训......
  • 基于共轭梯度法的 BP 网络学习改进算法详解
    基于共轭梯度法的BP网络学习改进算法详解一、引言BP(BackPropagation)神经网络是一种强大的机器学习工具,广泛应用于模式识别、函数逼近、数据分类等领域。然而,传统的BP算法在训练过程中存在一些问题,例如收敛速度慢、容易陷入局部最优解等。共轭梯度法作为一种高效的优......
  • 遗传算法工具箱详解
    遗传算法工具箱详解一、引言遗传算法作为一种强大的优化算法,在解决复杂的优化问题中得到了广泛应用。为了方便用户使用遗传算法,许多编程语言都提供了相应的遗传算法工具箱。这些工具箱集成了遗传算法的核心功能,包括种群初始化、适应度评估、选择、交叉、变异等操作,使用户......
  • 遗传算法原理与详解
    遗传算法原理与详解一、引言遗传算法(GeneticAlgorithm,GA)是一种基于自然选择和遗传学原理的优化搜索算法。它模拟生物进化过程中的遗传、变异、交叉等机制,在复杂的搜索空间中寻找最优解或近似最优解。遗传算法具有广泛的应用,包括函数优化、组合优化、机器学习、自动控制等......
  • Python设计模式详解之1 —— 单例模式
    单例模式(SingletonPattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点。单例模式适用于需要确保全局唯一实例的场景,例如配置管理、日志记录器、数据库连接等。1.单例模式的特点全局唯一性:在整个应用程序的生命周期内,单例类只能有一个实例。全局访问:......
  • Python设计模式详解之2 —— 工厂模式
    工厂模式(FactoryPattern)是一种创建型设计模式,旨在定义一个用于创建对象的接口,但由子类决定实例化哪个类。工厂模式可以帮助我们将对象的创建与其使用分离,增强代码的可扩展性和维护性。工厂模式的分类简单工厂模式(SimpleFactoryPattern)工厂方法模式(FactoryMethodPatte......