理解原子变量之二:从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开始学统计-数据类别与测量层次
数据分析前,我们首先要弄清楚数据的分类。数据并不仅仅是一堆数字和文字,它们实际上代表了我们看待事物属性的不同视角。从最宽泛的角度出发,我们可以将数据划分为定量(比如用数字表示)或者定性(例如&#…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
