内存栅栏 内存模型

2024-04-20 17:52
文章标签 内存 模型 栅栏

本文主要是介绍内存栅栏 内存模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

公众号:内存栅栏

先抛出代码:

atomic<int> a{0};atomic<int> b{0};int set(){    a.store(1,memory_order_relax);    b.store(2,memory_order_relax);int observe(){cout<<"a="a<<" b="<<b;}int main(){thead t1(set);thread t2(observe);t1.join();t2.join();}

问题:上面的代码, a和b的写操作, 顺序是怎样的?observe输出是什么?

仔细分析下,就能得出下面的几种情况:

  1. a=0 b =0

  2. a=1 b=2

  3. a=1 b=0

  4. a=0 b=2

   前三种情况都好理解,但是a=0 b=2这种情况,是不是感觉不好理解?为什么会出现a=0 b=2这种看起来不太合理的情况?这就涉及到内存栅栏了,内存模型的一致性问题。

我们都知道代码编译的时候,编译器会对代码进行优化,如gcc编译参数 :

  • -O0表示不做优化

  • -O1为默认优化

  • -O2除了10的优化以外,还做一些额外调整工作,例如指令调整等

  • -O3包括循环展开和其他一些处理特性相关的优化工作

    是的,编译器会对代码进行优化,如果编译器认定a、b的赋值操作语句的执行顺序对结果无任何影响的话,则可以根据情况将指令进行重排序以提高性能。如果a、b的赋值语句的执行必须是先a后b,则编译器不会进行优化。    除了编译器,甚至处理器也可能对指令的执行顺序打乱(通常这样的执行顺序都是超标量的流水线,即一个时钟周期内发射多条指令产生的),通常称为弱顺序(weak orderd)。这种情况下,b的赋值也完全可能在a赋值之前完成。当然,这和处理器架构有关,像我们平常用到的x86和SPARC都是强顺序内存模型,而采用Alpha、PowerPC、Itanlium、ArmV7这样的平台,都是弱顺序内存模型。

所以,上面的例子中,出现a=0, b=2就不足为奇了。一般情况下,出现这样的问题,程序员也很难想象是编译器或者处理器改变了代码的执行顺序导致错误的。

那么如何确保开启编译器优化的情况下,保证并发环境下,内存访问的正确性和一致性呢?幸运的是,我们有内存栅栏。

内存栅栏(Memory Barrier),之所以被称为“栅栏”,是因为它们在执行流中起到了隔离的作用,类似于现实生活中栅栏的功能,阻止某些事物通过。在计算机科学中,内存栅栏阻止指令重排越过这一“栅栏”,确保在栅栏一侧的操作(无论是读操作还是写操作)在逻辑上完全完成后,才能开始执行栅栏另一侧的操作。

1. 作用  

    ​​​​​​​​​​​​​​防止编译器和处理器进行过度的指令重排,确保在并发环境下内存访问的正确性和一致性。

在多处理器系统中,每个处理器可能有自己的缓存。处理器为了提高执行效率,会在执行程序时对指令进行重排。编译器同样也会在生成机器码时进行优化和指令重排。这些重排优化虽然提高了单线程代码的效率,但在多线程环境中可能导致数据不一致的问题。内存栅栏的引入,就是为了解决由于指令重排带来的内存可见性问题。

2. 类型

内存栅栏主要有以下几种类型:

  1. Load Barrier(加载栅栏):确保所有在栅栏之前的读操作完成后,才能执行栅栏之后的读操作。

  2. Store Barrier(存储栅栏):确保所有在栅栏之前的写操作完成后,才能执行栅栏之后的写操作。

  3. Full Barrier(全栅栏):结合加载栅栏和存储栅栏的功能,确保所有在栅栏之前的读写操作完成后,才能执行栅栏之后的读写操作。

3. C++中的内存栅栏

C++11标准引入了原子操作和内存模型的概念,其中就包括对内存栅栏的支持。C++提供的内存栅栏是通过原子操作库中的内存顺序参数来实现的:

  1. std::memory_order_relaxed:无同步或顺序制约。

  2. std::memory_order_acquire:本线程中,所有后续的读操作都必须在本原子操作完成后执行。

  3. std::memory_order_release:本线程中,所有之前的写操作完成后才能执行本原子操作。

  4. std::memory_order_acq_rel:同时具有acquire和release的效果。

  5. std::memory_order_consume: 本线程中,所有后续的有关本原子操作,必须在本原子操作完成后执行。

  6. std::memory_order_seq_cst:全栅栏,提供顺序一致的内存顺序。
     

那么上面的例子,如何确保赋值a一定在赋值b之前呢?

atomic<int> a{0};atomic<int> b{0};int set(){    a.store(1,memory_order_relax);    b.store(2,memory_order_release);//所有的写操作完成,才能执行本操作} int observe(){cout<<"a="a<<" b="<<b;}int main(){thead t1(set);thread t2(observe);t1.join();t2.join();}

结论

内存栅栏是确保多线程程序在现代多处理器系统中正确运行的关键机制。C++通过原子操作和明确的内存顺序模型提供了强大的工具来处理这一问题,开发者需要根据具体需求合理选择使用。

这篇关于内存栅栏 内存模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920942

相关文章

Python内存管理机制之垃圾回收与引用计数操作全过程

《Python内存管理机制之垃圾回收与引用计数操作全过程》SQLAlchemy是Python中最流行的ORM(对象关系映射)框架之一,它提供了高效且灵活的数据库操作方式,本文将介绍如何使用SQLAlc... 目录安装核心概念连接数据库定义数据模型创建数据库表基本CRUD操作创建数据读取数据更新数据删除数据查

Linux五种IO模型的使用解读

《Linux五种IO模型的使用解读》文章系统解析了Linux的五种IO模型(阻塞、非阻塞、IO复用、信号驱动、异步),重点区分同步与异步IO的本质差异,强调同步由用户发起,异步由内核触发,通过对比各模... 目录1.IO模型简介2.五种IO模型2.1 IO模型分析方法2.2 阻塞IO2.3 非阻塞IO2.4

k8s容器放开锁内存限制问题

《k8s容器放开锁内存限制问题》nccl-test容器运行mpirun时因NCCL_BUFFSIZE过大导致OOM,需通过修改docker服务配置文件,将LimitMEMLOCK设为infinity并... 目录问题问题确认放开容器max locked memory限制总结参考:https://Access

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件

java内存泄漏排查过程及解决

《java内存泄漏排查过程及解决》公司某服务内存持续增长,疑似内存泄漏,未触发OOM,排查方法包括检查JVM配置、分析GC执行状态、导出堆内存快照并用IDEAProfiler工具定位大对象及代码... 目录内存泄漏内存问题排查1.查看JVM内存配置2.分析gc是否正常执行3.导出 dump 各种工具分析4.

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.