【一分钟学C++】std::memory_order

在这里插入图片描述

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~
公众号: C++学习与探索  |  个人主页: rainInSunny  |  个人专栏: Learn OpenGL In Qt

文章目录

  • 写在前面
  • 为什么需要Memory Order
  • Memory Order
    • Relaxed Order
    • Release-Acquire Order

写在前面

  使用std::memory_order是用来限制编译器以及CPU对单线程当中的指令执行顺序进行重排的程度。这种限制,决定了以atom操作为基准点,对其之前的内存访问命令,以及之后的内存访问命令,能够在多大的范围内自由重排,从而形成了6种模式。这里我们主要讨论std::memory_order_relaxedstd::memory_order_acquirestd::memory_order_release。 注意,std::memory_order限制的是单线程中的CPU指令乱序,但一般用来解决多线程同步的问题。

为什么需要Memory Order

  如果不使用任何同步机制(例如mutex或atomic),在多线程中读写同一个变量,程序的结果是难以预料的。简单来说,编译器以及CPU的一些行为,会影响到程序的执行结果:

  • 即使是简单的语句,C++也不保证是原子操作。
  • CPU可能会调整指令的执行顺序。
  • 在CPU cache的影响下,一个CPU执行了某个指令,不会立即被其它CPU看见。
// 场景1:C++不保证线程2输出的是100,因为i = 100不是原子操作,可能存在中间态
int i = 0;
Thread_1:
i = 100;Thread_2:
std::cout << i;// 场景2:CPU可能会调整指令的执行顺序,这里假设所有操作都是原子操作,仍然可能输出0或者100
int x = 0;
int y = 0;Thread_1:
x = 100;
y = 200;Thread_2:
while (y != 200);
std::cout << x;// 场景3:假设A先于B,但CPU cache的影响下,Thread_2不能保证立即看到A操作的结果,所以Thread_2可能输出0或100
int x = 0;Thread_1:
x = 100; // AThread_2:
std::cout << x; // B

  场景1,i = 100;不是原子操作导致了结果不确定;场景2,CPU会在不影响当前线程执行逻辑情况下对指令执行顺序进行优化,如果Thread_1将y = 200调整到x = 100之前执行,那么可能输出0或者100;场景3,由于CPU缓存,可能导致Thread_2在输出的时候Thread_1中x的值还不可见,导致可能输出0或者100。

typedef enum memory_order {memory_order_relaxed, // relaxedmemory_order_consume, // consumememory_order_acquire, // acquirememory_order_release, // releasememory_order_acq_rel, // acquire/releasememory_order_seq_cst  // sequentially consistent
} memory_order;

  可以看出多线程读写变量需要同步机制,常见的有std::mutexstd::atomic,对比两者std::atomic性能要更好一些。C++标准库提供了std::memory_orderstd::atomic一起实现多线程之间同步,下面主要讲解std::memory_order_relaxedstd::memory_order_acquirestd::memory_order_release

Memory Order

Relaxed Order

std::atomic<int> x = 0;
std::atomic<int> y = 0;Thread_1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // BThread_2:
r2 = x.load(memory_order_relaxed); // C
y.store(66, memory_order_relaxed); // D

  执行完上面的程序,可能出现r1 == r2 == 66。理解这一点并不难,因为编译器允许调整C和D的执行顺序。如果程序的执行顺序是D -> A -> B -> C,那么就会出现r1 == r2 == 66。当只需要保证原子性,不需要其它同步操作时,选择使用std::memory_order_relaxed

Release-Acquire Order

  在这种模型下,store()使用std::memory_order_release,而load()使用std::memory_order_acquire。这种模型有两种效果,第一种是可以限制CPU指令的重排。除此之外,还有另一种效果:假设 Thread_1中store()的那个值成功被Thread_2中load()到了,那么Thread_1在store()之前对内存的所有写入操作,此时对Thread_2来说都是可见的。

  • 在store()之前的所有内存读写操作,不允许被移动到这个store()的后面。
  • 在load()之后的所有内存读写操作,不允许被移动到这个load()的前面。
#include <thread>
#include <atomic>
#include <cassert>
#include <string>std::atomic<bool> ready{ false };
int data = 0;
void producer()
{data = 66; // Aready.store(true, std::memory_order_release); // B
}
void consumer()
{while (!ready.load(std::memory_order_acquire)) // C;assert(data == 66); // D,never failed
}
int main()
{std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();return 0;
}

  由于在store()之前的所有内存读写操作,不允许被移动到这个store()的后面,所以线程t1中A操作一定在B操作之前就执行完毕了。线程t2中C操作跳出循环时意味着线程t1中B操作执行完毕,那么data此时肯定已经被赋值为66,所以assert永远不会失败。反之如果这里使用std::memory_order_relaxed,那么t1线程中data的赋值可能被CPU调整到store()后面,那就可能导致assert失败。

在这里插入图片描述


关注公众号:C++学习与探索,有惊喜哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52759.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧交通基于yolov8的行人车辆检测计数系统python源码+onnx模型+精美GUI界面

【算法介绍】 智慧交通中&#xff0c;基于YOLOv8的行人车辆检测计数系统是一项高效、准确的技术解决方案。该系统利用YOLOv8这一先进的目标检测算法&#xff0c;结合深度学习技术&#xff0c;能够实时检测并准确计数道路上的行人和车辆。YOLOv8在保证检测速度的同时&#xff0…

物联网——DMA+AD多通道

DMA简介 存储器映像 某些数据在运行时不会发生变化&#xff0c;则设置为常量&#xff0c;存在Flash存储器中&#xff0c;节省运行内存的空间 DMA结构图 DMA访问权限高于cpu 结构要素 软件触发源&#xff1a;存储器到存储器传输完成后&#xff0c;计数器清零 硬件触发源&…

基于SpringBoot的甜品店管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的蛋糕甜品店管理系…

ARM----时钟

时钟频率可以是由晶振提供的&#xff0c;我们需要高频率&#xff0c;但是外部接高的晶振会不稳定&#xff0c;所有使用PLL&#xff08;锁相环&#xff09;来放大频率。接下来就让我们学习用外部晶振提供的频率来配置时钟频率。 一.时钟源的选择 在这里我们选择外部晶振作为时钟…

Golang | Leetcode Golang题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; func integerReplacement(n int) (ans int) {for n ! 1 {switch {case n%2 0:ansn / 2case n%4 1:ans 2n / 2case n 3:ans 2n 1default:ans 2n n/2 1}}return }

Leetcode 109.有序链表转换二叉搜索树(Medium)

给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为 平衡 二叉搜索树。 示例 1: 输入: head [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0&#xff0c;-3,9&#xff0c;-10,null,5]&#xff0c;它表示所示的高度…

中秋假期也能及时回应客户:微信聚合管理系统,自动回复

中秋佳节是阖家团圆的日子&#xff0c;很多人选择在此期间休息放松。但作为一名职场人士&#xff0c;如何在假期中不遗漏客户咨询&#xff1f; 不妨试试这个WeBot助手&#xff0c;你可以进行微信自动回复设置&#xff0c;轻松实现假期与工作两不误。 同一界面聚合多个账号 通…

性能诊断的方法(四):自下而上的资源诊断方法和发散的异常信息诊断方法

关于性能诊断的方法&#xff0c;我们可以按照“问题现象—直接原因—问题根源”这样一个思路去归纳。我们先从问题的现象去入手&#xff0c;包括时间的分析、资源的分析和异常信息的分析。接下来再去分析产生问题现象的直接原因是什么&#xff0c;这里我们归纳了自上而下的资源…

Python爬虫使用实例-wallpaper

1/ 排雷避坑 &#x1f95d; 中文乱码问题 print(requests.get(urlurl,headersheaders).text)出现中文乱码 原因分析&#xff1a; <meta charset"gbk" />解决方法&#xff1a; 法一&#xff1a; response requests.get(urlurl,headersheaders) response.en…

Python异常处理:异常的捕获和处理(try, except, finally)①

文章目录 1. 异常处理的基本概念1.1 什么是异常&#xff1f;1.2 为什么需要异常处理&#xff1f; 2. Python异常处理语法2.1 try和except示例&#xff1a; 2.2 else示例&#xff1a; 2.3 finally示例&#xff1a; 3. 捕获多个异常示例&#xff1a; 4. 捕获所有异常示例&#xf…

反激电路中TL431光耦反馈参数的计算,环路设计思路

反馈的过程 当副边的输出电压升高时&#xff0c;TL431参考端电压&#xff08;R端&#xff09;电压也会升高&#xff0c;使得TL431的导通量增加&#xff0c;同时光耦内部的发光二极管流过的电流也会增大&#xff0c;进而使得光耦三极管导通量增加&#xff0c;相连的电源IC电压反…

【Linux】数据链路层

一、数据链路层引入 1.1 数据链路层的功能 在网络层中&#xff0c;我们使用IP协议进行通信&#xff0c;需要进行跨网络转发到目标主机&#xff0c;本质上就是一个报文经历了无数个子网&#xff0c;而数据链路层就是解决在一个子网中如何传输报文的问题。 数据链路层的功能是&a…

H5端接入萤石监控

官方文档 EZOPEN协议 下滑至-平台架构 web/h5端使用文档 <template><div :id"video-container${index}${index2}" class"w-full bg-black"></div> </template><script>export default {data() {return {EZVIZAToken:…

原型模式详细介绍和代码实现

&#x1f3af; 设计模式专栏&#xff0c;持续更新中&#xff0c; 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 Java实现原型模式 介绍: 原型模式&#xff08;Prototype Patte…

视频监控平台中的视频处理核心技术揭秘:自动化监控与智能预警成为趋势?

随着科技的飞速发展&#xff0c;视频监控作为安全管理与信息获取的重要手段&#xff0c;其技术也在不断革新与优化。一个高效的视频监控平台&#xff0c;不仅依赖于先进的硬件设备&#xff0c;更离不开强大的视频处理技术作为支撑。在众多视频监控平台中&#xff0c;安防监控Ea…

Autosar(Davinci) --- 创建一个S/R类型的port(下)

前言: 前面章节我们讲解了S/R类型的Port如何创建,这一章节,我们着重讲一下生成的代码,以及我们如何添加代码让这些门与灯之间的关系产生连接。 一、CtSaDoor.c 在【Rte.c】的【IO_TASK】中我们可以看到,反复的判断Rte_Ev_Cyclic_IO_Task_0_200ms这个条件是否成立,当200…

数据分析-前期数据处理

今天找到一份关于医学体检的数据&#xff0c;在数据分析前期工作需要对数据做处理&#xff0c;在这里我们对原始数据做一些处理&#xff0c;将数据处理为可分析的标准数据。下一篇文章做数据的分析。数据想要获取的话可以到我的资源下载。1 数据读取 import pandas as pd data…

Vue3 父组件向子组件传值:异步数据处理的显示问题

一、问题场景 假设我们有一个父组件和一个子组件&#xff0c;父组件需要经过一些复杂的计算或者异步操作才能得到要传递给子组件的值。在数据还没有准备好的时候&#xff0c;子组件尝试获取并显示这个值&#xff0c;这就可能导致子组件没有数据可显示或者显示了一个不正确的初…

rust + bevy 实现小游戏 打包成wasm放在浏览器环境运行

游戏界面 代码地址 github WASM运行 rustup target install wasm32-unknown-unknown cargo install wasm-server-runner cargo run --target wasm32-unknown-unknowncargo install wasm-bindgen-cli cargo build --release --target wasm32-unknown-unknown wasm-bindgen --…

开源 TTS 模型「Fish Speech」1.4 发布;GameGen-O :生成开放世界游戏视频模型丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。 我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、…