当前位置: 首页 > news >正文

多核时代下线程间的内存可见性

目录

      一  源起

二  互斥锁和内存可见性

三 原子操作和内存可见性

四  内存序

五  参考文章


  一  源起

       现代多核CPU通常具有多级缓存(L1、L2、L3等),每个核心可能有自己的私有L1和L2缓存,而L3缓存可能是共享的。在多核处理器系统中,每个核心可能会在其本地缓存中存储内存位置的副本。这可能导致一个核心上的线程修改了数据,而这个修改没有立即反映到其他核心的缓存中,从而导致缓存不一致为了解决这个问题,现代CPU使用缓存一致性协议(如MESI协议),确保多个CPU核心之间的缓存保持一致。当一个核心修改了它的缓存中的数据时,其他核心的缓存副本将被标记为无效,并在需要时从主内存中重新加载最新数据。这个过程就是保证了当一个线程修改了一个变量的值时其他线程可以立刻访问到修改后的值。

二  互斥锁和内存可见性

        mutex使我们最常用的互斥保护方式,使用mutex(互斥锁)可以确保在多线程环境下对共享数据的安全访问。当一个线程获取了mutex锁并修改了共享数据后,其他线程在获取到mutex锁后可以立刻看到更新后的值。这是因为mutex除了提供互斥访问的能力外,还有一个重要的特性就是内存屏障(Memory Barrier)。内存屏障可以防止CPU的指令重排,确保在mutex锁释放之前的所有内存写入操作都对其他线程可见。这就保证了当其他线程在获取到mutex锁后,可以立刻看到更新后的值。但是需要注意的是,这个行为是针对mutex锁的保护下的操作。如果有线程在没有获取mutex锁的情况下访问共享数据,那么就可能看到的是旧的数据,因为这种情况下没有内存屏障的保护。

       由此我们可以看到std::mutex本身并不会刷新CPU缓存。然而,当你在多线程环境中使用std::mutex来保护数据时,它可以确保在锁的保护下,对数据的修改对所有线程都是可见的。这是因为在获取和释放锁的过程中,会进行内存屏障操作,这将强制刷新CPU缓存,使得在锁保护下的数据修改对所有线程可见。

三 原子操作和内存可见性

       我们重点说原子操作的内存可见性,c++11提供了std::atomic原子操作,std::atomic提供了一种在多线程环境中对数据进行原子操作的方式。原子操作是不可中断的操作,一旦开始就会执行到结束,不会被其他线程打断。这就保证了在多线程环境中,使用std::atomic进行操作的数据在任何时刻都是一致的,不会出现因为线程切换导致的数据不一致的问题,保证一个变量写到一半儿被其他线程读到。并且std::atomic操作也会刷新CPU缓存。这是因为std::atomic提供了一种机制,确保在多线程环境中对特定对象的操作是原子的,即不可中断的。这意味着,一旦一个线程开始一个原子操作,它将在任何其他线程有机会访问该对象之前完成该操作。所以在多核处理器系统中,当一个原子操作完成时,处理器会刷新其缓存,以便其他处理器可以看到最新的数据。然而,这并不意味着所有的std::atomic操作都会导致缓存刷新。具体行为取决于所使用的内存顺序(memory order)。例如,std::memory_order_relaxed就不会导致缓存刷新,而std::memory_order_acquire、std::memory_order_release、std::memory_order_acq_rel和std::memory_order_seq_cst则会。

       所以并不是所有的std::atomic操作都有内存可见性的保证,其中std::memory_order_relaxed是最弱的内存序,它只保证了原子操作本身不会被重排,但不保证操作之间的顺序。也就是说,如果线程A在std::memory_order_relaxed模式下修改了一个std::atomic变量,线程B可能看不到这个修改,或者说,线程B看到的可能是一个旧的、未修改的值。

四  内存序

     最后说到另一个更加复杂的问题内存序,关于内存序前面已经有几个文章讨论这个问题

揭开内存屏障的面纱

c++原子操作的各种内存序

五  参考文章

mutex如何保证数据的一致性和正确性

 c++ std::atomic 数据可见性

C++的std::atomic与体系结构中多核内存模型/缓存一致性协议有什么关系?

Memory Model: 从多处理器到高级语言
 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [Meachines] [Easy] granny IIS 6.0+CVE-2017-7269+进程迁移+MS15-051权限提升
  • mov转mp4,这几款软件轻松转换格式!
  • OD C卷 - 幼儿园篮球游戏
  • 实现Kruskal算法连通游戏地图地牢
  • RTA-VRTE适配Orin
  • pdf查看密码
  • Android 14适配
  • Swift 6.0 如何更优雅的抛出和处理特定类型的错误
  • 每日一问:为什么MySQL索引使用B+树? 第4版 (含时间复杂度对比表格)
  • JVM感知docker容器内存资源限制
  • Blender----利用DEM(tif)生成三维模型
  • 解读红外控制遥控器原理!!!
  • Pytest框架直接右键运行 testcase.py,不执行最外层conftest
  • CACTER直播预告:聚焦EDLP邮件数据防泄露实战重点
  • 基于STM32+手机APP设计的智能停车场系统——程序源码原理图设计原理设计文档演示视频框图等(文末工程资料下载)
  • JavaScript 如何正确处理 Unicode 编码问题!
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 《Java编程思想》读书笔记-对象导论
  • 2017 前端面试准备 - 收藏集 - 掘金
  • CentOS7简单部署NFS
  • Cumulo 的 ClojureScript 模块已经成型
  • exports和module.exports
  • iOS编译提示和导航提示
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • linux安装openssl、swoole等扩展的具体步骤
  • python_bomb----数据类型总结
  • python学习笔记-类对象的信息
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • spring cloud gateway 源码解析(4)跨域问题处理
  • Vultr 教程目录
  • Yeoman_Bower_Grunt
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 测试开发系类之接口自动化测试
  • 订阅Forge Viewer所有的事件
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 官方解决所有 npm 全局安装权限问题
  • 那些被忽略的 JavaScript 数组方法细节
  • 使用 @font-face
  • 微信小程序填坑清单
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • AI算硅基生命吗,为什么?
  • UI设计初学者应该如何入门?
  • ​字​节​一​面​
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #define、const、typedef的差别
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (差分)胡桃爱原石
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (排序详解之 堆排序)
  • (十一)c52学习之旅-动态数码管
  • (一)Thymeleaf用法——Thymeleaf简介
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程