原型变量、原子操作、原子性、内存序

一、原子变量、原子操作

  • 锁竞争:互斥锁条件变量、原子变量、信号量、读写锁、自旋锁。
  • 在高性能基础组件优化的时候,为了进一步提高并发性能,可以使用原子变量。
  • 性能:原子变量 > 自旋锁 > 互斥锁
    • 操作临界资源的时间较长时使用互斥锁(粒度大)
    • 如果只操作单个变量的话,可以使用原子变量(粒度小) 进行优化。
  • 给基础类型或者指针加上一个标记 std::atomic<T> 之后它就是原子变量对这些变量的操作就是原子操作
  • 原子变量是一种多线程编程中常用的同步机制,它能确保对共享变量的操作在执行时不会被其它线程的操作干扰,从而避免竞态条件。
  • 原子变量具备原子性,也就是要么全部完成,要么全部未完成。
  • 为什么会使用到原子变量 ?
    • 通常某一个变量对应的操作,其 CPU 的指令是大于 1 个指令的,在多线程环境下,可能会引发竞态,在这种线程不安全的情况下,我们需要为这个变量单独设置一个锁安全的标记 std::atomic<T>从而实现线程安全
    • 多线程环境下,确保对共享变量的操作在执行时不会被干扰,从而避免竞态条件。
std::atomic<T>
is_lock_free; // 是否支持无锁操作
store(T desired, std::memory_order order); // 用于将特定的值存储到原子对象中
load(std::memory_order order); // 用于获取原子变量的当前值// 访问和修改包含的值,将包含的值替换并返回它原来的值。如果替换成功,则返回原来的值
exchange(std::atomic<T>* obj, T desired);
/* 
比较一个值和一个期望值是否相等,如果相等则该值替换成一个新值,并返回 true,否则不做任何操作并返回 false。
*/
compare_exchange_weak(T& expected, T val, memory_order success, memory_order failure);
/*
compare_exchange_weak 函数是一个弱化版本的原子操作函数,因为在某些平台上它可能会失败并重试。
如果需要保证严格的原子性,应该使用 compare_exchange_strong 函数
*/
compare_exchange_strong((T& expected, T val, memory_order success, memory_order failure);fetch_add
fetch_sub
fetch_and
fetch_or
fetch_xor

二、原子性

  • 要么都做要么还没做,不会让其它核心看到执行的一个中间状态
  • 单处理器单核心实现原子性:
    • 只需要保证操作指令不被打断。
      • 屏蔽中断 → 不允许操作指令被打断,不让其它线程看到操作指令的中间状态
      • 底层硬件自旋锁 → 为了实现线程切换。
  • 多处理器或多核心实现原子性:
    • 除了要保证操作指令不被打断,还需要避免其它核心操作相关的内存空间
      • 以往的 0x86 lock 指令,锁总线避免所有内存的访问
      • 现在的 lock 指令,锁总线只阻止其它核心对相关内存空间的访问,也就是只锁住相关内存空间
      • 内存总线:CPU 需要通过内存总线访问内存。
      • 磁盘总线:CPU 需要通过磁盘总线访问磁盘。
  • 存储体系结构
    • 为什么要有 CPU 缓存:为了解决 CPU 运算速度与内存访问速度不匹配的问题,在 CPU 和内存之间设置了一些缓存。
      CPU 和磁盘之间也有一个缓存 → 高速缓冲区(page cache) → 为了解决 CPU 运算速度与磁盘访问速度不匹配的问题。
      
    • L1 和 L2 是核心独有的,L3 是核心共有的,也就是多个核心共用一个 L3
    • CPU 缓存的基本单位:cache line,64 字节(64 B)。每次最少读取的数据空间(不管用户要读的数据空间有多小)。
      • flag:标识缓存中的数据是否可用,存储的是缓存的状态值(MESI)。
      • tag:索引数据是否在缓存中、数据在缓存中的位置。
      • data:具体存储的数据。

在这里插入图片描述

  • 在 CPU 缓存的基础上,CPU 如何读写数据 ? 注意:CPU 缓存中的数据永远比内存中的数据要新
    • 写直达策略
      • 每次写操作既会写到 CPU 缓存中,也会写到内存中,写性能会很低。
    • 写回策略(write-back)(现代 CPU 使用的策略)
      • 尽量避免每次写数据都把数据写到内存中
      • 写操作
        • 先检查是否命中 CPU 缓存,如果命中了就直接写,并标记为脏数据(CPU 缓存中的数据和内存中的数据不一致)
        • 如果没有命中,就需要在 CPU 缓存中找一块区域来存储新数据,也就是定位缓存块
          • 如果有可用的缓存块(还没有存储其它数据的缓存块) 就直接使用。
          • 如果没有可用的缓存块了,则采用 LRU 策略去定位缓存块
            • 该缓存块存储的数据是脏数据,先把这个缓存块中的数据刷到内存中,然后用这个缓存块去存储新数据,并标记为脏数据
            • 该缓存块存储的数据不是脏数据,直接将新数据写入到这个缓存块中,并标记为脏数据
      • 读操作
        • 先检查是否命中 CPU 缓存,如果命中了就直接返回。
        • 如果没有命中,从内存中读取数据并返回,然后定位缓存块去存储该数据。
          • 如果有可用的缓存块就直接使用。
          • 如果没有可用的缓存块了,则采用 LRU 策略去定位缓存块。
            • 该缓存块存储的数据是脏数据,先把这个缓存块中的数据刷到内存中,然后用这个缓存块去存储刚刚从内存中读取的数据,并标记为非脏数据
            • 该缓存块存储的数据不是脏数据,直接将刚刚从内存中读取的数据写入到这个缓存块中,并标记为非脏数据
  • 为什么会有缓存一致性问题 ?
    • CPU 是多核心的。
    • 基于写回策略将会出现缓存不一致的问题。
      • 数据在核心 0 的 CPU 缓存中,但是还没写到内存中;核心 2 的 CPU 缓存中没有该数据,于是会从内存中读取该数据,但是核心 2 从内存中读到的数据与核心 0 的 CPU 缓存中的数据不一致。
  • 如何解决缓存不一致的问题 ?
    • 写传播:总线嗅探(bus snooping)
      • 监听发布者模式:每一个核心都会监听总线上的写事件,当某个核心写数据的时候,会基于总线进行广播,其它的核心读到了这个事件之后,会自动修改自己的数据。
    • 事务的串行化:锁 + lock 指令
      • 多个核心对同一个缓存块进行读写操作的时候,必须要串行执行,否则会带来不确定性。
      • core0 写 i = 10,锁总线,必须等 i = 10 写操作结束后,core1 才能写 i = 20,这样 core2 读到的 i 就一定等于 20。

在这里插入图片描述

  • 优化:尽量减小写传播给总线带来的带宽压力
    • 两个策略:
      • 写传播:如何减少无效的监听,减小总线带宽的压力。
      • 串行化机制:如何锁总线。
  • MESI 一致性协议
    • 基于总线嗅探机制实现了事务串行化,通过状态机降低总线带宽的压力
    • 4 个状态
      • Modified:已修改,某个数据块已修改但是没有同步到内存中。
      • Exclusive:独占,某个数据块只在某核心的缓存中,并且此时缓存和内存中的数据是一致的。
      • Shared:共享,某个数据块在多个核心的缓存中,并且此时缓存和内存中的数据是一致的。
      • Invalidated:已失效,某个数据块在核心中已失效,不是最新的数据。
        • core0 中的数据:i = 5,core1 中的数据:i = 5,内存中的数据:i = 5;当 core0 写 i = 10 时,会通过总线嗅探,将 core1 中的数据 i = 5 的状态修改为 Invalidated。
    • 锁住 M 和 E 状态(因为 M 和 E 状态是不需要广播的),避免相关内存的访问

三、内存序

  • 原子性还没有解决避免竞态条件的问题
  • 为什么会有内存序问题
    • 编译器优化重排
      • C++ 在编译代码的时候,为了提高未来运行的效率,编译器会对代码指令进行编译优化重排。
    • CPU 指令优化重排
      • CPU 在运行的时候,也会对指令进行优化重排:在实现原子性的时候,会锁 M、E 状态,既然核心不能操作相关的内存区域,那就去操作不相关的内存区域,这样核心就不会干等着,从而提高整体的运行效率。
    • 在 CPU 看来,i 和 j 没有任何关系,可以并行处理。但程序的逻辑是:在 j += 2 的时候,i 已经 += 1了,在多线程条件下,就会出现竞态问题。
      int i = 0;
      int j = 0;
      i += 1;
      j += 2;
      
  • 内存序规定了什么
    • 规定了多个线程访问同一个内存地址时的语义
      • 同步性某个线程对内存地址的更新何时能被其它线程看见
      • 顺序性某个线程对内存地址访问附近可以做怎么样的优化
  • 内存模型
    • 这里所指的内存模型对应缓存一致性模型,作用是对同一时间的读写操作进行排序,在不同的 CPU 架构上,这些模型的具体实现方式可能不同,但是 C++ 11 屏蔽了内部细节,不用考虑内存屏障。可能有时使用的模型粒度比较大,会损耗性能,当然还是使用各平台底层的内存屏障粒度更准确,效率也会更高。
    • memory_order_relaxed:松散内存序。
      • 只用来保证对原子对象的操作是原子的,在不需要保证顺序时使用。
      • 读操作和写操作都可以使用。
      • 效率最高。
      // 没有同步性,其它线程可能读到的不是最新的值
      // 不干预 编译器或 CPU 的优化
      s.load(std::memory_order_relaxed);
      
      在这里插入图片描述
    • memory_order_release:释放操作。
      • 在写入某原子对象时,当前线程的任何前面的读写操作都不允许重排到这个操作的后面去并且保证其它线程可以读取到该原子对象的最新值
      • 通常与 memory_order_acquire 配对使用。
      • 只能在写操作中使用。
      • 依据前面的才写入
      // 具备同步性,其它线程读到的是最新的值
      // 干预了优化
      s.store(10, std::memory_order_release);
      
      在这里插入图片描述
    • memory_order_acquire:获取操作。
      • 在读取某原子对象时,当前线程的任何后面的读写操作都不允许重排到这个操作的前面去并且保证当前线程可以读取到该原子对象的最新值
      • 只能在读操作中使用。
      • 后面的依据读取的
      // 具备同步性,当前线程读到的是最新的值
      // 干预了优化
      s.load(std::memory_order_acquire);
      
      在这里插入图片描述
    • memory_order_acq_rel:获得释放操作。
      • 一个读 — 修改 — 写操作,同时具有获得语义和释放语义,即它前后的任何读写操作都不允许重排,并且保证其它线程可以读取到该原子对象的最新值、当前线程可以读取到该原子对象的最新值。
    • memory_order_seq_cst:顺序一致性语义。
      • 对于读操作相当于获得,对于写操作相当于释放,对于读 — 修改 — 写操作相当于获得释放。
      • 是所有原子操作的默认内存序,并且会对所有使用此模型的原子操作建立一个全局顺序,保证了多个原子变量的操作在所有线程里观察到的操作顺序相同。
      • 效率最低。
#include <atomic>
#include <thread>
#include <assert.h>
#include <iostream>// g++ relaxed.cc -o relaxed -lpthreadstd::atomic<bool> x, y;
std::atomic<int> z;void write_x_then_y()
{x.store(true,std::memory_order_relaxed);  // 1y.store(true,std::memory_order_relaxed);  // 2
}void read_y_then_x()
{while(!y.load(std::memory_order_relaxed));  // 3if(x.load(std::memory_order_relaxed))  // 4++z;
}
// z 会不会等于 1 ?  不一定int main()
{for (int i = 0; i < 100000; i++) {x = false;y = false;z = 0;std::thread b(read_y_then_x);std::thread a(write_x_then_y);b.join();a.join();int v = z.load(std::memory_order_relaxed);if (v != 1)std::cout << v << std::endl;}return 0;
}
#include <atomic>
#include <thread>
#include <assert.h>
#include <iostream>// g++ acquire_release.cc -o acquire_release -lpthreadstd::atomic<bool> x,y;
std::atomic<int> z;void write_x_then_y()
{x.store(true,std::memory_order_relaxed);  // 1 y.store(true,std::memory_order_release);  // 2   y = true x= true
}void read_y_then_x()
{while(!y.load(std::memory_order_acquire));  // 3 自旋,等待 y 被设置为trueif(x.load(std::memory_order_relaxed))  // 4++z;
}
// z 能确保读到 1int main()
{x = false;y = false;z = 0;std::thread a(write_x_then_y);std::thread b(read_y_then_x);a.join();b.join();std::cout << z.load(std::memory_order_relaxed) << std::endl;return 0;
}

四、互斥锁

  • 如何实现互斥锁:
    • 互斥锁首先需要做一个内存标记记录的是线程 ID,因为互斥锁需要进行线程切换。
    • 互斥锁是为了保护临界资源,同时只允许一个线程去访问临界资源,所以会有一个阻塞队列,通过阻塞队列唤醒其它线程去访问临界资源。
    • 要屏蔽中断。
    • 底层硬件自旋锁。
  • 互斥锁的表现:
    • 先在用户态自旋一会儿。
    • 获取失败,把任务挂起(放到阻塞队列中),核心会切换其它线程去执行。
    • 休眠一段时间再次尝试获取锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/300395.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7 个 iMessage 恢复应用程序/软件可轻松恢复文本

由于误操作、iOS 升级中断、越狱失败、设备损坏等原因&#xff0c;您可能会丢失 iPhone/iPad 上的 iMessages。意外删除很大程度上增加了这种可能性。更糟糕的是&#xff0c;这种情况经常发生在 iDevice 缺乏备份的情况下。 &#xff08;iPhone消息消失还占用空间&#xff1f;&…

如何利用HubSpot 出海CRM实现精准海外客户定位与拓展?

在当今全球化的商业环境中&#xff0c;企业寻求海外市场的拓展已成为增长的重要策略。然而&#xff0c;海外市场的复杂性和多样性为企业带来了巨大的挑战。为了有效地定位和拓展海外客户&#xff0c;许多企业选择了HubSpot 出海CRM作为他们的营销和销售管理工具。今天运营坛将带…

Web题记

反序列化补充知识&#xff1a; private变量会被序列化为&#xff1a;\x00类名\x00变量名 protected变量会被序列化为: \x00\*\x00变量名 public变量会被序列化为&#xff1a;变量名web254 这个逻辑不难&#xff0c;自己刚看的时候还奇怪是不是自己哪里想错了&#xff0c;因为…

java云his系统源码 B/S版+saas智慧医院云his系统源码 二甲医院应用多年 运行稳定

java云his系统源码 B/S版saas智慧医院云his系统源码 二甲医院应用多年 运行稳定 医院云HIS系统简介&#xff1a; SaaS模式Java版云HIS系统&#xff0c;在公立二甲医院应用三年&#xff0c;经过多年持续优化和打磨&#xff0c;系统运行稳定、功能齐全&#xff0c;界面布局合理…

mac电脑安装redis教程

1、下载地址 Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Redis 7.2 …https://redis.io/download/#redis-downloads 2、安装 2.1 解压下载后的压缩文件 2.2 进入…

【C++】类和对象(中篇)

目录 1、类中的6个默认成员函数 2、构造函数 2.1 概念 2.2 特性 3、析构函数 3.1 概念 3.2 特性 4、拷贝构造函数 4.1 概念 4.2 特征 5、赋值运算符重载 5.1 运算符重载 5.1.1 全局的operator ​编辑 5.1.2 成员函数的operator 5.2 赋值运算符重载 6、创建Date类…

ffmpeg 将多个视频片段合成一个视频

ffmpeg 将多个视频片段合成一个视频 References 网络视频 6 分钟的诅咒。 新建文本文件 filelist.txt filelist.txtfile output_train_video_0.mp4 file output_train_video_1.mp4 file output_train_video_2.mp4 file output_train_video_3.mp4 file output_train_video_4.m…

android 资源文件混淆

AGP7.0以上引用AndResGuard有坑 记录下 在项目的build.gradle中添加如下 buildscript {ext.kotlin_version "1.4.31"repositories {google()jcenter()maven {url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}}dependencies {class…

2023护网行动经验分享(2024护网招人)

今年的护网又开始摇人了&#xff0c;不知道大家有想法没&#xff1f; 去年的护网结束之后&#xff0c;朋友圈感觉是在过年&#xff0c;到处是倒计时和庆祝声。 看得出来防守方们7*24小时的看监控还是比较无奈的。 本次复盘基于我对整个护网行动的观察总结而来&#xff0c;仅…

C语言分支语句

一、什么是语句 C语句可分为以下五类&#xff1a; 表达式语句 函数调用语句 控制语句 复合语句 空语句 本周后面介绍的是控制语句。 控制语句用于控制程序的执行流程&#xff0c;以实现程序的各种结构方式&#xff0c;它们由特定的语句定义符组成&#xff0c;C语 言有…

建筑节能遮阳物件类网站织梦模板 节能建筑类网站源码下载(带手机版数据同步)

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 模板名称&#xff1a;(带手机版数据同步)建筑节能遮阳物件类网站织梦模板 节能建筑类网站源码下载 本套织梦模板采用织梦最新内核开发的模板&#xff0c;这款模板使用范围广&#xf…

RUST Rover 条件编译 异常处理

按官方处理发现异常 会报异常 error: failed to parse manifest at C:\Users\topma\RustroverProjects\untitled2\Cargo.toml 修改模式如下才能正常编译 网上说明 这样处理 https://course.rs/cargo/reference/features/intro.html RUST 圣经里描述 [features] print-a []…

JQuery(二)---【使用JQuery对HTML、CSS进行操作】

零.前言 JQuery(一)---【JQuery简介、安装、初步使用、各种事件】-CSDN博客 一.使用JQuery对HTML操作 1.1获取元素内容、属性 使用JQ可以操作元素的“内容” text()&#xff1a;设置或返回元素的文本内容html()&#xff1a;设置或返回元素的内容(包括HTML标记)val()&#…

Win UI3开发笔记(九)关于图标Win10乱码问题

1、最开始的问题&#xff0c;winui3 gallery软件的左侧全是乱码&#xff0c;使用icon的时候&#xff0c;设置name属性出现的全是乱码&#xff0c;所以开发涉及到这部分使用Text.Glyph属性。 2、后来出现的问题&#xff0c;靠 textbox右键有各种操作&#xff0c;前面的图标乱码…

卷积神经网络-批量归一化

卷积神经网络-批量归一化 批量归一化的原理批量归一化的优点批量归一化的应用批量归一化的实现TensorFlow实现&#xff1a;PyTorch实现&#xff1a; 总结 批量归一化&#xff08;Batch Normalization&#xff0c;简称BN&#xff09;是一种用于提高深度神经网络训练速度和稳定性…

深入浅出 -- 系统架构之分布式系统底层的一致性

在分布式领域里&#xff0c;一致性成为了炙手可热的名词&#xff0c;缓存、数据库、消息中间件、文件系统、业务系统……&#xff0c;各类分布式场景中都有它的身影&#xff0c;因此&#xff0c;想要更好的理解分布式系统&#xff0c;必须要理解“一致性”这个概念。 其实关于…

《QT实用小工具·十二》邮件批量发送工具

1、概述 源码放在文章末尾 该项目实现了邮件的批量发送&#xff0c;如下图所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef SMTPCLIENT_H #define SMTPCLIENT_H#include <QtGui> #include <QtNetwork> #if (QT_VERSION > QT_VERSION_CHECK(5,0,…

面试经典150题【141-150】

文章目录 面试经典150题【141-150】208.实现前缀树&#xff08;Trie树&#xff09;211. 添加与搜索单词-数据结构设计212.单词搜索II200.岛屿数量130.被围绕的区域133.克隆图399.除法求值&#xff08;未做&#xff09;拓扑排序207.课程表210.课程表II 面试经典150题【141-150】…

CTF之社工-初步收集

题目就一个刷钻网站&#xff08;假的&#xff09; 扫描一下目录 发现还有一个登录界面 时间多的可以爆破一下&#xff08;反正我爆不出来&#xff09;&#xff0c;接着我们下载那个压缩包看看 发现是一个钓鱼小软件 没发现什么有用的信息那我们就去wireshark看看数据包喽&#…

Francek Chen 的128天创作纪念日

目录 Francek Chen 的128天创作纪念日机缘收获日常成就憧憬 Francek Chen 的128天创作纪念日 Francek Chen 的个人主页 机缘 不知不觉的加入CSDN已有两年时间了&#xff0c;最初我第一次接触CSDN技术社区是在2022年4月的时候&#xff0c;通过学长给我们推荐了几个IT社区平台&a…