7_线程安全_线程间的内存可视性2缓存_内存屏障_读写排序

接着上次的问题------并不意味着最终的结果是"2"。

为什么?因为涉及到高速缓存与内存总线的工作细节。处理器可能有高速缓存,用来保存最近从主存中读出数据的拷贝的快速本地内存。在一个"回写"(write-back)高速缓存系统中,数据最初只是保存在高速缓存中,而在后面的时间写入(刷新)主存。在不保证写顺序的系统中,高速缓存中的数据块可能在任何处理器认为方便的时候写入主存。如果两个处理器向同一内存地址写不同的数据,则不同的数据将分别保存在二者的高速缓存中。最终两个值将写到主存中,但是是在随机的时刻写入,与写到相应的高速缓存中的顺序无关。

即使同一线程(或处理器)中的两个写操作也不需要在内存中表现相同的顺序。内存控制器可能发现以相反的顺序写入会更快或更方便。

问题还不仅局限于两个线程写内存。想像一下,一个线程在某个处理器上向内存中写入数据,而另一个线程在其他处理器上读相同的内存。好像很显然地,线程将得到最近写入的值,而在有些硬件上的确如此。这种现象有时被称为"内存一致性"或"读写排序"。但是在处理器之间确保这种同步是复杂的,它会降低内存系统的速度,并且对大部分代码没有任何意义。现在很多的计算机(通常是最快的计算机)不再保证不同处理器间内存访问的顺序,除非程序使用特殊的指令,这些指令就是常说的"内存屏障"(memory barrier)。

内存屏障确保:所有在内存屏障之前发起的内存访问,必须先于在设置屏障之后发起的内存访问之前完成。

内存屏障是一堵移动的墙,而不是刷新cache的命令。

即使没有读写排序和内存屏障,写内存好像也必须是原子性的操作。即任何其他线程只能看到完整的旧数据或者看到完整的新数据。但这也并不总是正确的。大部分计算机有一个天然的内存粒度,依赖于内存的组织和总线结构。即使处理器读写8位数据,内存传输可能以32位或64位为单位。这意味着,相对于其他覆盖相同32或64位数据单元的内存操作而言,8位写操作不是原子操作。大部分计算机会写包含修改数据的整个内存数据单元(如32位)。如果两个线程向同一个32位内存单元中写入不同的8位数据,则结果是最后写入的线程将确定两个8位字节的值,即覆盖第一个线程写入的值。图3.8显示了这种效果。

聪明的你是不是想到了一种情况------如果一个变量跨越了内存单元的界限(这将在允许对齐内存访问的机器上出现),计算机不得不在两次总线事物中传送数据。例如,一个非对齐的32位数据可能需要写两个相邻的32位内存单元。如果其中一个内存单元同时被另一个处理器写入,则将丢失一半数据。这被称为"word tearing",如图3.9所示。

欢迎关注,持续前进。

相关推荐
好好研究几秒前
SpringBoot小案例打包执行流程
java·spring boot·后端
代码游侠4 分钟前
学习笔记——ESP8266 WiFi模块
服务器·c语言·开发语言·数据结构·算法
三不原则7 分钟前
故障案例:模型推理响应慢,排查 Redis 缓存集群问题
数据库·redis·缓存
wsx_iot8 分钟前
缓存问题相关
缓存
行者969 分钟前
Flutter跨平台开发适配OpenHarmony:进度条组件的深度实践
开发语言·前端·flutter·harmonyos·鸿蒙
rgeshfgreh12 分钟前
Spring Bean管理机制深度解析
java·spring boot·spring
ling-4513 分钟前
ssm-day07 springboot3、Mybatis-Plus、springboot实战
java·spring boot·后端
DYS_房东的猫14 分钟前
《 C++ 零基础入门教程》第3章:结构体与类 —— 用面向对象组织代码
开发语言·c++
向量引擎16 分钟前
复刻“疯狂的鸽子”?用Python调用Sora2与Gemini-3-Pro实现全自动热点视频流水线(附源码解析)
开发语言·人工智能·python·gpt·ai·ai编程·api调用
少许极端18 分钟前
算法奇妙屋(二十三)-完全背包问题(动态规划)
java·算法·动态规划·完全背包