曹大谈内存重排

写这篇文章的原因很简单,公司内部的 Golang 社区组织了第一期分享,主讲嘉宾就是我们敬爱的曹大。这个必定是要去听的,只是曹大的讲题非常硬核,所以提前找他要了参考资料,花了 1 个小时提前预习,才不至于在正式分享的时候什么也不懂。当然了,这也是对自己和主讲者的尊重。所有的参考资料都在文章最后一部分,欢迎自行探索。

在我读曹大给我的中英文参考资料时,我发现英文的我能读懂,读中文却很费劲。经过对比,我发现,英文文章是由一个例子引入,循序渐进,逐步深入。跟着作者的脚步探索,非常有意思。而中文的博客上来就直奔主题,对于第一次接触的人非常不友好。

两者就像演绎法和归纳法区别。国内的教材通常是演绎法,也就是上来先讲各种概念、原理,再推出另一些定理,比较枯燥;国外的教材更喜欢由例子引入,步步深入,引人入胜。这里,不去评判孰孰劣。多看看一些英文原版材料,总是有益的。据我所知,曹大经常从亚马逊上购买英文书籍,这个侧面也可以反映曹大的水平高啊。据说英文书一般都很贵,可见曹大也是很有钱的。

所以啊,技术文章写好不容易,我也自省一下。

话不多说,贴上文章的目录:

640?wx_fmt=png

什么是内存重排

分两种,硬件和软件层面的,包括 CPU 重排、编译器重排。

CPU 重排

引用参考资料 【内存一致模型】 里的例子:

640?wx_fmt=png

在两个线程里同时执行上面的代码,A 和 B 初始化值都是 0,那最终的输出是什么?

先说几种显而易见的结果:

执行顺序 输出结果
1-2-3-4 01
3-4-1-2 01
1-3-2-4 11
1-3-4-2 11

从 01 的排列组合来看,总共有4种:00、01、10、11。表格中还差两种:10、00。我们来重点分析下这两种结果究竟会不会出现。

首先是 10,假设 (2) 输出 1,(4) 输出 0。那么首先给 2,3 排个序:(3) -> (2),因为先要将 B 赋值为 1,(2) 才能打印出 1;同理,(4) -> (1)。另外,因为先打印 1,所以 (2) 要在 (4) 前面,合起来:(3) -> (2) -> (4) -> (1)。(2) 竟然在 (1) 前面执行了,不可能的!

那我们再分析下 00,要想打印 00,打印语句必须在相应变量赋值前执行:

640?wx_fmt=png

图中箭头表示先后顺序。这就尴

<think>嗯,用户问的是内存屏障如何防止指令重排。首先,我需要回忆一下计算机组成原理和操作系统的相关知识。记得指令重排是为了优化性能,但有时候会导致多线程下的问题。内存屏障的作用应该和这个有关。 接下来,我需要确定用户的理解水平。他们可能对底层机制不太熟悉,所以得用比较易懂的例子来解释。比如用生产者和消费者的场景,或者简单的变量读写来演示问题。 然后,内存屏障具体是怎么工作的呢?可能分为读屏障和写屏障,或者全屏障。需要说明不同类型的屏障如何限制重排的方向,比如后面的指令不能越过屏障提前执行,或者前面的不能延后。 还要考虑内存屏障在不同层面的应用,比如编译器在生成代码时可能重排指令,而CPU在执行时也会重排内存屏障需要在这两个层面都起作用,确保指令顺序符合预期。 另外,用户可能对为什么需要防止重排有疑问。应该举例说明如果没有屏障,可能出现数据竞争、状态不一致等问题,比如双重检查锁定中的单例模式失效。 需要注意的是,不能把内存屏障和原子操作混为一谈,虽然它们都用于并发控制,但机制不同。内存屏障主要是顺序保证,而原子操作关注操作的不可分割性。 可能还需要提到一些实际的编程例子,比如在Java中使用volatile变量会自动插入内存屏障,或者在C++中使用特定的内存序参数。 最后,总结内存屏障的重要性,强调它在多线程环境下确保可见性和顺序性的作用,避免因为优化带来的意外结果。</think>内存屏障(Memory Barrier)防止指令重排的含义,可以用一个简单的**生产-消费模型**来理解: 假设存在以下代码顺序: 1. 生产者准备数据(data = 123) 2. 设置就绪标志(ready = true) 3. 消费者检测到ready为true后读取data 💥**没有内存屏障时**: 编译器/CPU可能将指令重排为: 1. 先设置ready = true 2. 再准备data = 123 这会导致消费者看到ready=true时,data还没有准备好! 🔒**插入内存屏障后**: ```cpp // 生产者 data = 123; // 写操作 std::atomic_thread_fence(std::memory_order_release); // 写屏障 ready = true; // 标志位写入 // 消费者 while(!ready); // 等待标志 std::atomic_thread_fence(std::memory_order_acquire); // 读屏障 int val = data; // 安全读取 ``` 此时屏障确保: 1. 屏障前的所有写操作(data=123)必须完成 2. 屏障后的操作(ready=true)不能提前到屏障前 3. 消费者读取数据时,必须刷新内存视图 📌内存屏障的三种主要类型: 1. **写屏障**(Store Barrier):保证屏障前的所有写操作先于屏障后的写操作完成 2. **读屏障**(Load Barrier):保证屏障后的读操作不会重排到屏障前 3. **全屏障**(Full Barrier):同时具备读+写屏障功能 💡实际应用场景: - 无锁数据结构实现 - 双重检查锁定模式 - 跨核共享数据同步 - 硬件寄存器操作 通过这种强制顺序保证,既保持了CPU/编译器的优化能力,又确保了关键操作顺序的正确性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值