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

news2024/11/15 9:32:46

在这里插入图片描述

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~
公众号: 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; // A

Thread_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, // relaxed
    memory_order_consume, // consume
    memory_order_acquire, // acquire
    memory_order_release, // release
    memory_order_acq_rel, // acquire/release
    memory_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); // B

Thread_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; // A
    ready.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.coloradmin.cn/o/2135754.html

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

相关文章

day45-测试平台搭建之前端vue学习-基础4

目录 一、生命周期 1.1.概念 1.2.常用的生命周期钩子 1.3.关于销毁Vue实例 1.4.原理​编辑 1.5.代码 二、非单文件组件 2.1.组件 2.2.使用组件的三大步骤 2.3.注意点 2.4.关于VueComponent 2.5.一个重要的内置关系 三、今日学习思维导图 一、生命周期 1.1.概念 1).又名&…

每日OJ_牛客_点击消除(栈)

目录 牛客_点击消除&#xff08;栈&#xff09; 解析代码 牛客_点击消除&#xff08;栈&#xff09; 点击消除_牛客题霸_牛客网 描述&#xff1a; 牛牛拿到了一个字符串。 他每次“点击”&#xff0c;可以把字符串中相邻两个相同字母消除&#xff0c;例如&#xff0c;字符…

【机器学习】10——logistic的直观理解

机器学习10——logistic的直观理解 logistic 目录 机器学习10——logistic的直观理解训练过程具体例子 训练过程 数据集: 特征: 学习时间&#xff08;例如&#xff0c;1小时、2小时等&#xff09;。 标签: 是否通过考试&#xff08;0 或 1&#xff09;。 模型结构: 输入: 学习…

OpenAI推出o1系列模型:AI思考力爆表,带来全新智能体验

OpenAI的——o1系列模型&#xff0c;传说中的「草莓」&#xff0c;终于来与大家见面了&#xff01; 这个新模型可不一般&#xff0c;它可以进行复杂的推理&#xff0c;就像在认真思考一样&#xff0c;不再是简单的回答问题。CEO奥特曼称&#xff0c;这是一个全新的开始。它不仅…

智能照明监控系统在发电厂的应用

0前言 国内发电厂普遍使用传统照明控制方法&#xff0c;包括配电箱集中控制、就地开关控制和自动控制。然而&#xff0c;随着技术进步&#xff0c;这些方法已无法满足对安全、舒适、便捷、信息交互和节能环保的需求。因此&#xff0c;实施智能照明控制系统变得必要&#xff0c…

考研报名确认上传身份证户口本学历证明照片如何压缩裁剪

随着考研季节的到来&#xff0c;数以万计的考生开始准备报名所需的各种材料。在这一过程中&#xff0c;证件照片的上传无疑是一个关键环节。正确的照片格式和尺寸不仅能确保报名流程的顺利进行&#xff0c;还能避免因材料不合格而造成的不必要麻烦。本文将详细介绍如何在考研报…

密码学基础--ECDSA算法入门

目录 1.ECDSA签名长度的疑惑 2.ECDSA原理 2.1 生成签名 2.2 验签过程 2.3 签名编码问题 3.小结 1.ECDSA签名长度的疑惑 我们来看看ECDSA签名长什么样子&#xff0c;使用MuscleV02自动生成密钥对&#xff0c;并对message"0x11223344”进行签名&#xff0c;结果如下&a…

一款超级给力的DAW软件flstudio24.1.1.4285最新破解版!

嗨&#xff0c;音乐制作爱好者们&#xff01;今天要跟大家安利一款超级给力的DAW软件&#xff0c;它就是——fl studio24.1.1.4285最新破解版&#xff01; fl studio24.1.1.4285最新破解版简介&#xff1a;这款强大的数字音频工作站软件&#xff0c;是音乐人梦寐以求的工具。它…

Python+Pytest框架,“api_key.py文件怎么编写“?

1、在"api_keyword"文件夹下新增"api_key.py" import allure import requests import json import jsonpath from deepdiff import DeepDifffrom config import *allure.title("测试用例执行") class ApiKey:allure.step(">>>:开…

【win工具】win安装flameshot并设置截图快捷键

1.下载flameshot软件2.windows端配置flameshot快捷键3.取消win自带截图快捷键 1.下载flameshot软件 https://flameshot.org/#download installer版本为安装包 portable版本为免安装版 2.windows端配置flameshot快捷键 https://cloud.tencent.com/developer/article/2114952 W…

Java项目: 基于SpringBoot+mybatis+maven课程答疑系统(含源码+数据库+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven课程答疑系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

Pikachu靶场之csrf

CSRF 跨站请求伪造 CSRF入门及靶场实战 - FreeBuf网络安全行业门户 攻击者伪造恶意链接&#xff0c;诱使用户点击&#xff0c;这个链接附带了用户的认证凭据Cookie、Session等&#xff0c;执行操作如转账。 因为带了cookie、session&#xff0c;服务器认为是用户的行为。借用…

尚品汇-订单拆单、支付宝关闭交易、关闭过期订单整合(五十)

目录&#xff1a; &#xff08;1&#xff09;拆单接口 &#xff08;2&#xff09;取消订单业务补充关闭支付记录 &#xff08;3&#xff09;支付宝关闭交易 &#xff08;4&#xff09;查询支付交易记录 &#xff08;5&#xff09;PaymentFeignClient 远程接口 &#xff08…

玩转扩展库,温湿度传感器篇!—合宙Air201资产定位模组LuatOS快速入门05

随着LuatOS快速入门系列教程的推出&#xff0c;小伙伴们学习热情高涨。 合宙Air201不仅支持三种定位方式&#xff0c;还具有丰富的扩展功能&#xff0c;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期&#xff0c…

electron-vite vue3离线使用monaco-editor

目录 1.搭建一个 electron-vite 项目 2.安装monaco-editor和vite-plugin-monaco-editor 3.electron.vite.config.mjs配置 4.创建 worker.js并在main.js 引入 5.创建组件 MonacoVite.vue 组件 6. App.vue中引入组件 7.运行测试 1.搭建一个 electron-vite 项目 pnpm creat…

如何在算家云搭建TripoSR(三维重建)

一、模型介绍 TripoSR是由Tripo AI和Stability AI合作开发的先进开源模型&#xff0c;能在短时间内从单张图片生成高质量 3D 模型。 利用大型重建模型&#xff08;LRM&#xff09;的原理&#xff0c;TripoSR带来了关键的进步&#xff0c;大大提高了3D重建的速度和质量。模型的…

秒验HarmonyOS NEXT集成指南

开发工具&#xff1a;DevEco Studio 集成方式&#xff1a;在线集成 HarmonyOS API支持&#xff1a;> 12 集成前准备 注册账号 使用MobSDK之前&#xff0c;需要先在MobTech官网注册开发者账号&#xff0c;并获取MobTech提供的AppKey和AppSecret&#xff0c;详情可以点击查看…

基于SpringBoot+Vue的校内跑腿业务管理系统

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

使用 QEMU 模拟器运行 FreeRTOS 实时操作系统

文章目录 QEMU 官网QEMU 文档QEMU 简介QEMU 安装QEMU 命令启动虚拟机串口控制台监控命令行 FreeRTOS安装编译工具FreeRTOS 源码RISC-V-Qemu-virt_GCC 示例编译 RISC-V-Qemu-virt_GCC启动虚拟机运行 FreeRTOS QEMU 官网 https://www.qemu.org/ QEMU 文档 https://www.qemu.or…

爆品只是日百商家的表面“风光”

前不久&#xff0c;#大学生买爆台州商家的移动收纳筐#的话题上了热搜。现在的大学生相比以前讲究多了&#xff0c;虽然生活费没涨多少&#xff0c;但生活一定要精致。比如&#xff0c;在有限的预算内买各种收纳神器&#xff0c;把宿舍整理地井井有条。 爆品&#xff0c;往往成…