内存栅栏 内存模型

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

相关文章

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

详解如何使用Python从零开始构建文本统计模型

《详解如何使用Python从零开始构建文本统计模型》在自然语言处理领域,词汇表构建是文本预处理的关键环节,本文通过Python代码实践,演示如何从原始文本中提取多尺度特征,并通过动态调整机制构建更精确... 目录一、项目背景与核心思想二、核心代码解析1. 数据加载与预处理2. 多尺度字符统计3. 统计结果可

SpringBoot整合Sa-Token实现RBAC权限模型的过程解析

《SpringBoot整合Sa-Token实现RBAC权限模型的过程解析》:本文主要介绍SpringBoot整合Sa-Token实现RBAC权限模型的过程解析,本文给大家介绍的非常详细,对大家的学... 目录前言一、基础概念1.1 RBAC模型核心概念1.2 Sa-Token核心功能1.3 环境准备二、表结

Java内存区域与内存溢出异常的详细探讨

《Java内存区域与内存溢出异常的详细探讨》:本文主要介绍Java内存区域与内存溢出异常的相关资料,分析异常原因并提供解决策略,如参数调整、代码优化等,帮助开发者排查内存问题,需要的朋友可以参考下... 目录一、引言二、Java 运行时数据区域(一)程序计数器(二)Java 虚拟机栈(三)本地方法栈(四)J

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI