理解原子变量之二:从volatile到内存序-进一步的认识
目录
实例1
实例2
实例3
内存序中两个最重要的概念
补记
结论
实例1
看下面的例子:在vs2013中建立如下工程:
#include <thread>
#include <iostream>
#include <chrono>bool done = false;void worker(){std::this_thread::sleep_for(std::chrono::seconds(3));done = true;
}int main(){std::thread workerThread(worker);while (!done){//std::cout << "waiting" << std::endl;不知为什么,加上这句打印就可以跳出循环}std::cout << "thread finished" << std::endl;workerThread.join();std::cin.get();return 0;
}
使用release模式编译该工程,且优化采用O2
结果是程序卡住了,本来3秒钟就可以跳出while(!done)死循环,现在一直停着。
vs2013 release 模式下多线程卡住演示
实例2
实例1为什么会卡住呢?因为编译器在打开优化的情况下,“武断”的认为done永远都是false。编译器意识不到子线程也在改变done的取值,所以就认为done是常量,而不会在运行到while()时读取done的实际值,于是while(!done)就变成了while(true)。假如采用debug模式编译,编译器不会“自作聪明”,程序在运行时将老老实实的不断读取done的实时值,直到done==true为止。所以,debug模式不会卡住,而release模式会卡住(除非在编译时禁止优化)。
现在稍微修改一下代码,用volatile修饰 bool done。即使在release下编译,程序仍然正常走完,不卡住:

这是因为,volatile在提示编译器,done是一个不能被优化掉的变量。即使在release模式下,每次走到while时,也要查看done的值。
回到原子变量的话题上来,再看第三个例子:
实例3
现在用atomic<bool>代替volatile bool。
#include <thread>
#include <iostream>
#include <chrono>
#include <atomic>std::atomic<bool> done(false);void worker() {std::this_thread::sleep_for(std::chrono::seconds(3));done.store(true);
}int main() {std::thread workerThread(worker);while (!done.load()) {//std::cout << "waiting" << std::endl;不知为什么,加上这句打印就可以跳出循环}std::cout << "thread finished" << std::endl;workerThread.join();std::cin.get();return 0;
}
结果与使用volatile一样,while(!done.load())不会一直阻塞程序向下运行:

看到了上面的三个例子,接下来就要请出原子变量里面一个非常重要的概念:内存序。
内存序中两个最重要的概念
std::memory_order - cppreference.com列出了C++11标准里的六个内存序(memory_order)。这6个内存序的作用将在下一篇文章描述。其中多次出现一个词汇“可见”(visible)

以MEMORY_ORDER_ACQUIRE为例,其中文解释是:
有此内存定序的加载操作,在其影响的内存位置进行获得操作:当前线程中读或写不能被重排到此加载之前。其他线程的所有释放同一原子变量的写入,能为当前线程所见
“其他线程的所有释放同一原子变量的写入,能为当前线程所见”这句话的意思是什么?我们只要理解了什么是不可见,就理解了可见。对照实例1,在不使用volatile的情况下,子线程workerThread对done的修改,没有影响到主线程的while(!done)逻辑,这就是说,子线程对done的写入,不为主线程可见。
反观实例2和3,子线程修改了done,主线程的运行随之受了影响,这就是可见。
内存序里有两个重要的概念,一是指令排序,在下一篇里面将介绍;另一个就是可见性(visibility)。从例子2和3可见,在可见性这方面,原子变量的作用与volatile是一样的:原子变量被一个线程改变后,另一个线程肯定会发现这个变化。
补记
实例1,2,3都是在vs2013下演示的。在vs2019下,实例1效果有区别:vs2019的while循环在示例1中同样不会阻塞。这是因为,不同的编译器遵循不同的c++标准。即使遵循相同的C++标准,不同的编译器对同一个标准的贯彻程度也不一样:有的编译器严格遵循标准,既不多做,也不少做;有的编译器在某些次要方面可能达不到C++标准的要求(少做);还有的编译器为了安全性等方面考虑,甚至超过了C++标准的要求(多做)。vs2019很可能属于超过标准这一种:即使是多线程编程中使用普通的非原子变量,非易失(volatile)变量,仍能保证可见性(个人猜测)。
但无论如何,本文探讨的是C++11标准的内存序,不针对具体编译器。在遵守C++11标准方面,VS2013似乎比2019更贴切,所以三个实例均采用2013示范。
结论
前文解释了原子变量的第一个功能:它相当于粒度很细的互斥锁。本文介绍了原子变量的第二个功能:可见性。原子变量保证了多线程编程中的可见性。事实上,原子变量共负担了三个功能。下一篇文章将介绍其第三个功能--限制指令重排。
相关文章:
理解原子变量之二:从volatile到内存序-进一步的认识
目录 实例1 实例2 实例3 内存序中两个最重要的概念 补记 结论 实例1 看下面的例子:在vs2013中建立如下工程: #include <thread> #include <iostream> #include <chrono>bool done false;void worker(){std::this_thread::sle…...
DICOM标准:MR图像模块属性详解——磁共振成像(MR)在DICOM中的应用
目录 引言 磁共振成像(MR) 一、MR图像模块 二、MR图像属性描述 1、图像类型 (Image Type) 2、抽样每个象素 (Sampling per Pixel) 3、光度插值 (Photometric Interpretation) 4、位分配 (Bits Allocated) 结论 引言 数字成像和通信在医学(…...
Linux内核与用户空间
Linux内核与用户空间是Linux操作系统中的两个重要概念,它们各自承担着不同的功能和职责,并通过特定的机制进行交互。以下是对Linux内核与用户空间的详细解释: 一、Linux内核 定义:Linux内核是Linux操作系统的核心组件,…...
计算机网络-以太网小结
前导码与帧开始分界符有什么区别? 前导码--解决帧同步/时钟同步问题 帧开始分界符-解决帧对界问题 集线器 集线器通过双绞线连接终端, 学校机房的里面就有集线器 这种方式仍然属于共享式以太网, 传播方式依然是广播 网桥: 工作特点: 1.如果转发表中存在数据接收方的端口信息…...
找树根和孩子c++
题目描述 给定一棵树,输出树的根root,孩子最多的结点max以及他的孩子 输入 第一行:n(0<结点数<100),m(0<边数<200)。 以下m行;每行两个结点x和y…...
植物源UDP-糖基转移酶及其分子改造-文献精读75
植物源UDP-糖基转移酶及其分子改造 摘要 糖基化能够增加化合物的结构多样性,有效改善水溶性、药理活性和生物利用度,对植物天然产物的药物开发至关重要。UDP-糖基转移酶(UGTs)能够催化糖基从活化的核苷酸糖供体转移到受体形成糖苷键,植物中天然产物的糖基化修饰主要通过UGTs实…...
Redis中String 的底层实现是什么?
Redis中String 的底层实现是什么? Redis 是基于 C 语言编写的,但 Redis 的 String 类型的底层实现并不是 C 语言中的字符串(即以空字符 \0 结尾的字符数组),而是自己编写了 SDS(Simple Dynamic String&…...
像mysql一样查询es
先简单介绍一下这个sql查询,因为社区一直反馈这个Query DSL 实在是太难用了。大家可以感受一下下面这个es的查询。 GET /my_index/_search { “query”: { “bool”: { “must”: [ { “match”: { “title”: “search” } }, { “bool”: { “should”: [ { “te…...
SpringBoot中@Validated或@Valid注解校验的使用
文章目录 SpringBoot中Validated或Valid注解校验的使用1. 添加依赖2. 使用示例准备2-1 测试示例用到的类2-2 实体Dto,加入校验注解2-2 Controller 3. 示例测试4. Valid 和 Validated注解详解4-1 常用规则注解4-2 分组验证4-2-1 示例准备4-2-2 Controller接口4-2-3 P…...
HashMap为什么线程不安全?
一、Put操作(数据覆盖) HashMap底层是基于数组 链表(在 Java 8 以后,当链表长度超过一定阈值时会转换为红黑树)的数据结构。在多线程环境下,当多个线程同时对HashMap进行put操作时,可下面这种…...
类加载器及反射
目录 1.类加载器 1.1类加载【理解】 1.2类加载器【理解】 1.2.1类加载器的作用 1.2.2JVM的类加载机制 1.2.3Java中的内置类加载器 1.2.4ClassLoader 中的两个方法 2.反射 2.1反射的概述【理解】 2.2获取Class类对象的三种方式【应用】 2.2.1三种方式分类 2.2.2示例…...
aws boto3 下载文件
起因:有下载 aws s3 需求,但只有web 登录账号,有 id 用户名 密码,没有 boto3 的 key ID 经过分析,发现网页版有个地址会返回临时 keyID,playwright 模拟登录,用 page.on 监测返回数据ÿ…...
3DDFA-V3——基于人脸分割几何信息指导下的三维人脸重建
1. 研究背景 从二维图像中重建三维人脸是计算机视觉研究的一项关键任务。在虚拟现实、医疗美容、计算机生成图像等领域中,研究人员通常依赖三维可变形模型(3DMM)进行人脸重建,以定位面部特征和捕捉表情。然而,现有的方…...
求串长(不使用任何字符串库函数)
问题描述 编写一个程序,输入一个字符串,输出串的长度。 要求: (1)字符串长度不超过100个字符。 (2)不使用任何字符串库函数,建议使用堆串存储结构。 输入描述 输入一个字符串。 …...
第02章 MySQL环境搭建
一、MySQL的卸载 如果安装mysql时出现问题,则需要将mysql卸载干净再重新安装。如果卸载不干净,仍然会报错安装不成功。 步骤1:停止MySQL服务 在卸载之前,先停止MySQL8.0的服务。按键盘上的“Ctrl Alt Delete”组合键࿰…...
linux系统编程 man查看manual.stat
获取文件属性,(从inode结构体中获取) stat/lstat 函数 int stat(const char *path, struct stat *buf); 参数: path: 文件路径 buf:(传出参数) 存放文件属性,inode结构体…...
从网络到缓存:在Android中高效管理图片加载
文章目录 在Android应用中实现图片缓存和下载项目结构使用 代码解析关键功能解析1. 图片加载方法2. 下载图片3. 保存图片到缓存4. 文件名提取 总结 首先我们需要在配置AndroidManifest.xml里面添加 <uses-permission android:name"android.permission.INTERNET" …...
【数据结构】链表详解:数据节点的链接原理
链表(Linked List)是一种基础的数据结构,是程序设计中用来存储数据的典型方法之一。链表特别适合插入和删除操作频繁的场景,是了解数据结构和算法的基础。本文将从零开始,带大家了解链表的底层原理、类型(单…...
使用AWS Redshift从AWS MSK中读取数据
Amazon Redshift 流式摄取的目的是简化将流式数据直接从流式服务摄取到 Amazon Redshift 或 Amazon Redshift Serverless 的过程。 官方文档[1]中有详细步骤。用unauthenticated, IAM 的方式均可以进行连接,只不过使用的是不同端口:9092或者9098 [1] h…...
从0开始学统计-数据类别与测量层次
数据分析前,我们首先要弄清楚数据的分类。数据并不仅仅是一堆数字和文字,它们实际上代表了我们看待事物属性的不同视角。从最宽泛的角度出发,我们可以将数据划分为定量(比如用数字表示)或者定性(例如&#…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...
