Rust 中的Relaxed 内存指令重排演示:X=0 Y=0 是怎么出现的?
🔥 Rust 中的内存重排演示:X=0 && Y=0 是怎么出现的?
在并发编程中,我们经常会听说“内存重排(Memory Reordering)”这个术语,但它似乎总是只出现在理论或者别人口中的幻觉里。本文将通过一段简短却强大的 Rust 代码,来实际观察一次可能只发生在 CPU 和编译器角落的“神秘现象”。
🧪 实验目标
我们想要验证这样一件事:
在两个线程中,即使我们明确写入了某个变量的值,另一个线程仍可能读不到这个值,甚至两个线程都没看到对方的写入!
这似乎违背常识,但在 Relaxed 的内存模型下,这种事情确实会发生。
🧬 测试代码
我们从一个经典的“双线程数据交换”模型出发,使用 Rust 中的原子类型构造以下测试:
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::Arc;
use std::thread;// 两个变量
static A: AtomicI32 = AtomicI32::new(0);
static B: AtomicI32 = AtomicI32::new(0);// 两个线程读取的结果
static X: AtomicI32 = AtomicI32::new(0);
static Y: AtomicI32 = AtomicI32::new(0);#[test]
fn t() {let mut count = 0;loop {A.store(0, Ordering::Relaxed);B.store(0, Ordering::Relaxed);X.store(0, Ordering::Relaxed);Y.store(0, Ordering::Relaxed);let t1 = thread::spawn(|| {// 操作2 可能被重排到操作1前面A.store(1, Ordering::Relaxed); // 操作1let y = B.load(Ordering::Relaxed); // 操作2X.store(y, Ordering::Relaxed); // 操作3});let t2 = thread::spawn(|| {// 操作5 可能被重排到操作4前面B.store(1, Ordering::Relaxed); // 操作4let x = A.load(Ordering::Relaxed); // 操作5Y.store(x, Ordering::Relaxed); // 操作6});// 可能的结果:// t1 执行完了 t2 再执行 结果:X:0 Y:1// t2 执行完了 t1 再执行 结果:X:1 Y:0// t1 执行过程中,t2 也执行 结果:X:1 Y:1// t2 执行过程中 , t1 也执行 结果:X:1 Y:1// t1 t2 同时执行,在多核cpu上 一个线程一个核// 结果也是 X:1 Y:1// 等待线程结束 才往下执行t1.join().unwrap();t2.join().unwrap();let x = X.load(Ordering::Relaxed);let y = Y.load(Ordering::Relaxed);count += 1;if x == 0 && y == 0 {println!("🔥 Reordering observed after {} iterations! X={}, Y={}", count, x, y);break;}if count % 1_000_000 == 0 {println!("Still testing... {} iterations", count);}}
}
运行
cargo test "memOrder::Relaxed::t" --release -- --exact --nocapture
# 不建议使用debug来查看,如果没有优化,很难看到
# "memOrder::Relaxed::t" 是模块的路径。这里得看你的文件是如何写的。可以问问AI你的应该如何填写
# --release 使用优化的版本。不是debug的版本
# --nocapture 禁止捕获输出,这样可以看到test的输出语句
复现了 在第1百万次之后

🤯 为什么 X0 && Y0 会发生?
✅ 预期行为
我们按常理推理,至少会有一个线程看到另一个线程的写入。可能的结果包括:
X=1, Y=1:两边都看到写入(最常见)X=1, Y=0或X=0, Y=1:一个看到,一个没看到- ❌
X=0, Y=0:理论上“不可能”,却实实在在发生了
🧠 背后发生了什么?
这是内存模型中最令人着迷的部分。
1️⃣ 编译器重排
在 Relaxed 模式下,编译器允许重新排序读写操作,比如:
// 原代码:
A.store(1);
let y = B.load();// 实际可能被编译器优化为:
let y = B.load();
A.store(1);
2️⃣ CPU 层级的乱序执行
即便编译器没优化,CPU 在执行指令时也可能提前执行 load 操作(出于缓存命中率、流水线等优化考虑)。
3️⃣ 多核之间的缓存不可见性
每个 CPU 核心有自己的缓存,如果线程运行在不同核心上,而缓存同步还没完成,就会出现这种“看不到对方写入”的情况。
🚨 为什么这很危险?
如果你在业务代码中用 Relaxed 来实现同步(比如某种状态机、标志位),那你可能看到的状态并不是你以为的那样。更严重的,在某些极端时机下,程序可能出现奇怪的崩溃、断言失败,或者数据一致性问题。
✅ 如何避免这种问题?
使用合适的内存顺序,比如:
- 使用
Ordering::Release写入 - 使用
Ordering::Acquire读取
这可以确保“写操作在读取操作前发生”,编译器和 CPU 都不会乱排:
A.store(1, Ordering::Release);
let y = B.load(Ordering::Acquire);
这样写可以防止 x == 0 && y == 0 这种现象出现。
🧵 延伸:这个实验很有意思!
你可以试着:
- 多跑一会儿,看看多少次能复现一次
0, 0 - 改成
SeqCst观察结果 - 用 barrier(内存屏障)代替 Ordering
- 在不同平台(ARM vs x86)测试效果(ARM 更容易复现)
相关文章:
Rust 中的Relaxed 内存指令重排演示:X=0 Y=0 是怎么出现的?
🔥 Rust 中的内存重排演示:X0 && Y0 是怎么出现的? 在并发编程中,我们经常会听说“内存重排(Memory Reordering)”这个术语,但它似乎总是只出现在理论或者别人口中的幻觉里。本文将通过…...
C++进程间通信开发实战:高效解决项目中的IPC问题
C进程间通信开发实战:高效解决项目中的IPC问题 在复杂的软件项目中,进程间通信(Inter-Process Communication, IPC)是实现模块化、提高系统性能与可靠性的关键技术之一。C作为一门高性能的编程语言,广泛应用于需要高效…...
FPGA-DDS技术的波形发生器
1.实验目的 1.1掌握直接数字频率合成(DDS)的基本原理及其实现方法。 1.2在DE2-115 FPGA开发板上设计一个可调频率的正弦波和方波发生器,频率范围10Hz~5MHz,最小分辨率小于1kHz。 1.3使用Quartus II进行仿真,并通过S…...
C#实现通过MQTT Broker——EMQX发布订阅消息及其认证、授权的安全配置操作
一、准备内容 MQTT的构成、使用场景、工作原理介绍-CSDN博客文章浏览阅读656次,点赞7次,收藏12次。MQTT(Message Queuing Telemetry Transport)是一种轻量级、基于发布-订阅模式的消息传输协议【适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境】它在物联网应用中…...
考研单词笔记 2025.04.15
addition n添加,增加,加法 additional a附加的,额外的 in addition 另外;除此之外;加之 augment v增加,增强,提高 explode v急剧增长,爆炸,爆发,迸发 gr…...
【双指针】三数之和(medium)
三数之和(medium) 题⽬描述:解法(排序双指针):算法思路:C 算法代码:Java 算法代码:注:数组转列表 题⽬链接:15. 三数之和 题⽬描述: …...
【项目管理】第17章 项目干系人管理-- 知识点整理
项目管理-相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 (一)知识总览 项目管理知识域 知识点: (项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域) 对应:第6章-第19章 第6章 项目管理概论 4分第13章 项目资源管理 3-4分第7章 项目…...
视频融合平台EasyCVR可视化AI+视频管理系统,打造轧钢厂智慧安全管理体系
一、背景分析 在轧钢厂,打包机负责线材打包,操作人员需频繁进入内部添加护垫、整理包装、检修调试等。例如,每班产线超过300件,12小时内人员进出打包机区域超过300次。若员工安全意识薄弱、违规操作,未落实安全措施就…...
无参数RCE
无参数RCE(Remote Code Execution,远程代码执行) 是一种通过利用目标系统中的漏洞,在不直接传递用户可控参数的情况下,实现远程执行任意代码的攻击技术。与传统的RCE攻击不同,无参数RCE不依赖外部输入参数…...
鸿蒙开发中,@Extend、@Styles 和 @Builder 的区别
在鸿蒙(HarmonyOS)开发中,Extend、Styles 和 Builder 是三种常用的装饰器,用于提升代码复用性和可维护性。以下是它们的详细介绍和示例: 1. Extend:扩展组件样式 说明: 功能:用于…...
C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读
目录 0.为什么需要智能指针? 1.智能指针的使用及原理 RAII: 智能指针的原理: 2.智能指针有哪些? std::auto_ptr std::unique_ptr std::shared_ptr std::weak_ptr 0.为什么需要智能指针? 想要回答这个问题&…...
Vue接口平台学习七——接口调试页面请求体
一、实现效果图及简单梳理 请求体部分的左边,展示参数,分text和file类型。 右边部分一个el-upload的上传文件按钮,一个table列表展示,一个显示框,用于预览选择的文件,点击可大图展示。 二、页面内容实现 …...
小程序css实现容器内 数据滚动 无缝衔接 点击暂停
<view class"gundongBox"><!-- 滚动展示信息的模块 --><image class"imgWid" :src"imgurlgundong.png" mode"widthFix"></image><view class"gundongView"><view class"container&qu…...
内存条装机,无法启动
1、i5-9600k,主板技嘉z390gamingx: ①、插满4条DDR4-2666,无法启动; ②、两条DDR4-2666,插在2、4或者1、3插槽,可以启动; ③、三条DDR4-2666,一条DDR4-2400,插满4个内存插槽&…...
【力扣】day1
文章目录 27.移除元素26. 删除有序数组的重复项 27.移除元素 26. 删除有序数组的重复项 我们仔细看一下这两道题的最后的返回值,为什么第一题返回slow 而第二题返回slow1 最后的返回值该如何返回绝对不是凭感觉,我们自己分析一下第一个slow,从0位置开始, 遇到val值就开始和fas…...
图像预处理-色彩空间补充,灰度化与二值化
一.图像色彩空间转换 1.1 HSV颜色空间 HSV颜色空间使用色调(Hue)、饱和度(Saturation)和亮度(Value)三个参数来表示颜色 一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本…...
linux如何用关键字搜索日志
在 Linux 系统中搜索日志是日常运维的重要工作,以下是几种常用的关键字搜索日志方法: 1. 基础 grep 搜索 bash 复制 # 基本搜索(区分大小写) grep "keyword" /var/log/syslog# 忽略大小写搜索 grep -i "error&…...
Spring Boot项目中结合MyBatis实现MySQL的自动主从切换
原理解析 1. MySQL主从复制(Master-Slave Replication) 工作原理:MySQL主从复制通过二进制日志(binary log)来同步数据。主服务器记录所有更改操作到二进制日志中,从服务器读取这些日志并执行相应的SQL语…...
项目交接时信息遗漏,如何预防
项目交接时,信息遗漏可能导致任务延误、质量下降和团队混乱,因此,建立系统化的交接流程和使用专业的工具是防止信息遗漏的有效策略。交接过程中的信息丢失往往源自沟通不畅、文档不完整或者责任不明确等问题,这不仅影响项目的顺利…...
【AI量化第24篇】KhQuant 策略框架深度解析:让策略开发回归本质——基于miniQMT的量化交易回测系统开发实记
我是Mr.看海,我在尝试用信号处理的知识积累和思考方式做量化交易,应用深度学习和AI实现股票自动交易,目的是实现财务自由~ 目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。 本篇要讲到量化的核心了——策略。说白了每个投资者…...
向量数据库Qdrant 安装 不使用docker
一、导读 环境:Ubuntu 24.04、Windows 10、WSL 2、Qdrant 1.13.4 背景:换了新工作,使用qdrant作为向量库,需要不使用docker安装 时间:20250415 说明:初入职,不了解,暂且记下 二、…...
微电网与分布式能源:智能配电技术的场景化落地
安科瑞顾强 随着数字化转型与能源革命的加速推进,电力系统正经历从传统模式向智能化、网络化方向的深刻变革。用户侧的智能配电与智能用电技术作为这一变革的核心驱动力,正在重塑电力行业的生态格局。本文将从技术架构、应用场景及未来趋势等维度&#…...
实战指南:封装Whisper为FastAPI接口并实现高并发处理-附整合包
实战指南:封装Whisper为FastAPI接口并实现高并发处理 下面给出一个详细的示例,说明如何使用 FastAPI 封装 OpenAI 的 Whisper 模型,提供一个对外的 REST API 接口,并支持一定的并发请求。 下面是主要步骤和示例代码。 1. 环境准备…...
C#Winform程序将子窗体嵌入容器方法
private void OpenForm(Form childFrom) { //首先判断容器中是否有其他的窗体 foreach (Control item in this.panelRight.Controls) { if (item is Form) { ((Form)item).Close(); } } //嵌入新的窗体 childFrom.TopLevel false;//将子窗体设置成非顶级控件 childFrom.Parent…...
Web三漏洞学习(其一:文件上传漏洞)
靶场:云曦历年考核题 一、文件上传 在此之前先准备一个一句话木马 将其命名为muma.txt 23年秋期末考 来给师兄上个马 打开环境以后直接上传muma.txt,出现js弹窗,说明有前端验证 提示只能上传.png .jpg 和 .gif文件,那就把muma.txt的后缀…...
37-串联所有单词的子串
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如,如果 words ["ab","cd","ef"], 那么 "abcdef…...
ShardingSphere复合分片之hash槽算法
前言 上一篇《ShardingSphere复合分片》中有详细介绍多key多value的复合分片算法应该如何设计,在大部分情况下该算法是没有问题的,但是一旦涉及到数据迁移时,该算法的缺点就暴露无疑了。 为满足日益增长的用户或者订单的需求,在分…...
Web三漏洞学习(其二:sql注入)
靶场:NSSCTF 、云曦历年考核题 二、sql注入 NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过,但为了学习sql,整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判…...
KrillinAI:视频跨语言传播的一站式AI解决方案
引言 在全球内容创作领域,跨语言传播一直是内容创作者面临的巨大挑战。传统的视频本地化流程繁琐,涉及多个环节和工具,不仅耗时耗力,还常常面临质量不稳定的问题。随着大语言模型(LLM)技术的迅猛发展,一款名为Krillin…...
gravity`(控制 View 内部内容的对齐方式)
文章目录 **1. 常用取值****示例** **2. layout_gravity(控制 View 在父容器中的对齐方式)****常用取值****示例** **3. gravity vs layout_gravity 对比****4. 注意事项****5. 总结** 作用对象:当前 View 的内部内容(如 TextView…...
