c++11--原子操作,顺序一致性,内存模型
1.原子操作
多线程下为了实现对临界区资源的互斥访问,最普遍的方式是使用互斥锁保护临界区。
然而,如果临界区资源仅仅是数值类型时,对这些类型c++提供了原子类型,通过使用原子类型可以更简洁的获得互斥保护的支持。
(1). 一个实例
#include <atomic>
#include <thread>
#include <iostream>
using namespace std;atomic_llong total{0};
void func(int){for(long long i = 0; i < 100000; ++i){total += i;}
}int main(){thread t1(func, 0);thread t2(func, 0);t1.join();t2.join();cout << total << endl;return 0;
}
上述由于使用了atomic_llong类型的原子变量,所以 total += i;操作是具备互斥保护的。
(2). cstdatomic中的原子类型和内置类型对应表
| 原子类型名称 | 对应的内置类型名称 |
|---|---|
| atomic_bool | bool |
| atomic_char | char |
| atomic_schar | signed char |
| atomic_uchar | unsigned char |
| atomic_int | int |
| atomic_uint | unsigned int |
| atomic_short | short |
| atomic_ushort | unsigned short |
| atomic_long | long |
| atomic_ulong | unsigned long |
| atomic_llong | long long |
| atomic_ullong | unsigned long long |
| atomic_char16_t | char16_t |
| atomic_char32_t | char32_t |
| atomic_wchar_t | wchar_t |
(3).另一种使用原子类型的方式
使用std::atomic<T>模板类。
注意点:
a.该模板类不支持拷贝构造,移动构造,赋值运算符。
b.std::atomic<T>定义了到T的类型转换函数。
(4).atomic类型及其相关的操作
操作 | atomic_flag | atomic_bool | atomic_integral_type | atomic<bool> | atomic<T*> | atomic<integral-type> | Atomic<class-type> |
|---|---|---|---|---|---|---|---|
test_and_set | Y | ||||||
clear | Y | ||||||
is_lock_free | y | y | y | y | y | y | |
load | y | y | y | y | y | y | |
store | y | y | y | y | y | y | |
exchange | y | y | y | y | y | y | |
compare_exchange_weak+strong | y | y | y | y | y | y | |
fetch_add,+= | y | y | y | ||||
fetch_sub,-= | y | y | y | ||||
fetch_or,|= | y | y | |||||
fetch_and,&= | y | y | |||||
fetch_xor,^= | y | y | |||||
++,-- | y | y | y | y |
(5).使用atomic_flag可自行实现自旋锁
#include <thread>
#include <atomic>
#include <iostream>
#include <unistd.h>
using namespace std;std::atomic_flag lock = ATOMIC_FLAG_INIT;
void f(int n){while(lock.test_and_set())cout << "waiting from thread " << n << endl;cout << "thread " << n << " starts working" << endl;
}void g(int n){cout << "thread " << n << " is going to start." << endl;lock.clear();cout << "thread " << n << " starts working" << endl;
}int main(){lock.test_and_set();thread t1(f, 1);thread t2(g, 2);t1.join();usleep(100);t2.join();return 0;
}
2.顺序一致性,内存模型
默认下,使用原子类型时,自然就是顺序一致的。即,指令实际被cpu执行的顺序,和高级语言中书写顺序是一致的。
有时,对某些并发场景,我们可能并不需要如此严格的限制,也能保证指令执行的正确性,我们可以借助顺序一致性,内存模型的显式控制来达到此目的。
一个实例
#include <thread>
#include <atomic>
#include <iostream>
using namespace std;atomic<int> a{0};
atomic<int> b{0};
int ValueSet(int){int t = 1;a = t;b = 2;
}int Observer(int){cout << "(" << a << ", " << b << ")" << endl;
}int main(){thread t1(ValueSet, 0);thread t2(Observer, 0);t1.join();t2.join();cout << "Got (" << a << ", " << b << ")" << endl;return 0;
}
上述实例中线程t1依次对a,b执行赋值。线程t2依次读取a,b的值。
但从高级语言到处理器执行二进制指令的实际效果并不一定严格按上述预期的顺序来。
从高级语言到处理器执行二进制指令有两个阶段会影响指令实际执行的顺序:
(1). 编译阶段
编译器处于性能优化考虑,针对没有执行依赖的语句可能生成汇编代码时调整指令顺序。
上述实例在执行汇编时,线程t1中 int t = 1;a = t;和b = 2;没有依赖关系,所以,允许安排汇编语句时,b = 2;对应的汇编语句在int t = 1;a = t;对应的汇编语句之前或中间。线程t2中访问a,访问b类似。
顺序一致性指的是编译后的汇编指令顺序和高级语言中顺序是否一致。
(2).二进制指令执行阶段
假设编译器按高级语言一致顺序产生了如下汇编代码
1 Loadi reg3, 1; #将立即数1放入寄存器reg3
2 Move reg4, reg3; #将reg3的数据放入reg4
3 Store reg4, a; #将寄存器reg4中的数据存入内存地址a
4 Loadi reg5, 2; #将立即数2放入寄存器reg5
5 Store reg5, b; #将寄存器reg5中的数据存入内存地址b
处理器实际执行二进制指令时由于上述1,2,3和4,5没有依赖关系,所以某些cpu体系结构下,4,5可能在1,2,3之前或1,2,3中间被执行。
这里,我们称严格按二进制指令顺序执行指令的cpu体系结构为强顺序的,反之,则为弱顺序的。
所以,内存模型是一个针对cpu体系结构的概念。
弱顺序体系结构下,保证指令执行顺序符合预期的手段是添加额外的汇编指令。
1 Loadi reg3, 1; #将立即数1放入寄存器reg3
2 Move reg4, reg3; #将reg3的数据放入reg4
3 Store reg4, a; #将寄存器reg4中的数据存入内存地址a
Sync
4 Loadi reg5, 2; #将立即数2放入寄存器reg5
5 Store reg5, b; #将寄存器reg5中的数据存入内存地址b
由于添加了额外的Sync汇编指令,即使在弱内存cpu体系结构下执行上述汇编指令,也能保证先执行1,2,3再执行4,5。
像Sync这样的汇编指令称为:内存栅栏。
3.高级语言如何保证指令执行顺序和预期(代码中出现顺序)一致
(1).编译阶段保证得到的汇编指令顺序和高级语言中一致。
(2).针对强顺序cpu体系结构,无需额外处理。针对弱顺序cpu体系结构,在汇编指令中额外插入内存栅栏。
默认情况下,使用原子操作时,上述(1),(2)均是满足的。
4.通过放松一致性要求来提高执行效率
c++的原子操作大多都可以使用memory_order作为一个参数。
c++11中memory_order所有可能取值:
| 枚举值 | 定义规则 |
|---|---|
| memory_order_relaxed | 不对执行顺序做任何保证 |
| memory_order_acquire | 本线程中,所有后续读操作必须在本条原子操作完成后执行 |
| memory_order_release | 本线程中,所有之前的写操作完成后才能执行本条原子操作 |
| memory_order_acq_rel | memory_order_acquire + memory_order_release |
| memory_order_consume | 本线程中,所有后续的有关本原子类型的操作,必须在本条原子操作完成后执行 |
| memory_order_seq_cst | 全部存取操作都按顺序执行 |
memory_order_seq_cst 是c++11所有原子操作默认值。具备最强一致性要求。
通常,可把atomic的成员函数可使用的memory_order分为三组:
(1). 原子存储操作(store)
memory_order_relaxed 、memory_order_release 、memory_order_seq_cst
(2).原子读取操作(load)
memory_order_relaxed、memory_order_consume、memory_order_acquire 、memory_order_seq_cst
(3).同时读写操作
全部六种
5.利用显式设置memory_order保证原子操作既快又对的实例
5.1.默认版本
#include <thread>
#include <atomic>
#include <iostream>
using namespace std;atomic<int> a;
atomic<int> b;
int Thread1(int){int t = 1;a = t;b = 2;
}void Thread2(int){while(b != 2);cout << a << endl;
}int main(){thread t1(Thread1, 0);thread t2(Thread2, 0);t1.join();t2.join();return 0;
}
上述t2种预期打印出来的a应该是1。
原子操作默认下会保证严格的顺序一致性(编译层面,cpu体系执行层面),若我们希望维持预期下,放松一致性要求就需要通过显式设置memory_order来达到目的。
5.2.一个一致性要求略低但保证符合预期的版本
#include <thread>
#include <atomic>
#include <iostream>
using namespace std;atomic<int> a;
atomic<int> b;
int Thread1(int){int t = 1;a.store(t, memory_order_relaxed);b.store(2, memory_order_release);
}int Thread2(int){while(b.load(memory_order_acquire) != 2);cout << a.load(memory_order_relaxed) << endl;
}int main(){thread t1(Thread1, 0);thread t2(Thread2, 0);t1.join();t2.join();return 0;
}
t1中memory_order_release会保证a的写入先执行,再执行b的写入。
t2中memory_order_acquire会保证先读取b,再读取a。
上述两个限制下,我们知道t2中a将会符合预期。
相关文章:
c++11--原子操作,顺序一致性,内存模型
1.原子操作 多线程下为了实现对临界区资源的互斥访问,最普遍的方式是使用互斥锁保护临界区。 然而,如果临界区资源仅仅是数值类型时,对这些类型c提供了原子类型,通过使用原子类型可以更简洁的获得互斥保护的支持。 (1). 一个实例…...
【数据结构】栈和队列(队列的基本操作和基础知识)
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343🔥 系列专栏:《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 目录 前言 队列 队列的概念和结构 队列的…...
设计模式——适配器模式(Adapter Pattern)
概述 适配器模式可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口和抽象目标类接口。适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装…...
测试C#使用OpenCvSharp从摄像头获取图片
OpenCvSharp也支持获取摄像头数据,不同于之前测试AForge时使用AForge控件显示摄像头数据流并从中截图图片,OpenCvSharp中显示摄像头数据流需要周期性地从摄像头中截取图片并显示在指定控件中。本文学习C#使用OpenCvSharp从摄像头获取图片的基本方式。 …...
【基础】【Python网络爬虫】【12.App抓包】reqable 安装与配置(附大量案例代码)(建议收藏)
Python网络爬虫基础 App抓包1. App爬虫原理2. reqable 的安装与配置reqable 安装教程reqable 的配置 3. 模拟器的安装与配置夜神模拟器的安装夜神模拟器的配置配置代理配置证书 4. 内联调试及注意事项软件启动顺开启抓包功reqable面板功列表部件功能列表数据快捷操作栏 夜神模拟…...
LabVIEW在电机噪声与振动探测的应用
LabVIEW在电机噪声与振动探测的应用 硬件部分是电机噪声和振动测试分析系统的基础,主要由三大核心组件构成:高灵敏度振动传感器、先进的信号调理电路和高性能数据采集卡。这些设备协同工作,确保了从电机捕获的噪声和振动信号的准确性和可靠性…...
编码器是什么,以光电编码器为例,说明一下光电编码器的名字由来,结构,原理,特点,用处
问题描述: 问题解答: 定义:编码器是一种测量角度、位置、速度等物理量的传感器,它可以将物理量转换成电信号,以便计算机或控制系统进行处理和控制。编码器通常由码盘和光电转换器组成,码盘上刻有若干条码道…...
MySQL:主从复制
准备两台服务器:安装好mysql mysql1:192.168.2.222 master mysql2:192.168.2.226 slave 1、主从服务器分别作以下 1.1、版本一致 1.2、初始化表,并在后台启动mysql 1.3、修改root的密码 2、修改主服务器master #vi /etc/my…...
【K8S 二进制部署】部署Kurbernetes的网络组件、高可用集群、相关工具
目录 一、K8S的网络类型: 1、K8S中的通信模式: 1.1、、pod内部之间容器与容器之间的通信 1.2、同一个node节点之内,不同pod之间的通信方式: 1.3、不同node节点上的pod之间是如何通信的呢? 2、网络插件一ÿ…...
Ubuntu 常用命令之 locate 命令用法介绍
🔥Linux/Ubuntu 常用命令归类整理 locate命令是在Ubuntu系统下用于查找文件或目录的命令。它使用一个预先构建的数据库(通常由updatedb命令创建)来查找文件或目录,因此它的查找速度非常快。 plocate 安装 locate 不是 Ubuntu 系统的原生命令/功能,要想在 Ubuntu 系统中…...
java中file类常用方法举例说明
java中file类常用方法举例说明 当使用 java.io.File 类时,以下是一些常用方法的举例说明: 创建文件或目录: // 使用路径名创建File实例 File file new File("C:\\Users\\UserName\\Documents\\example.txt");// 使用父路径和子路…...
机器学习分类模型
机器学习常见分类模型及特点 机器学习常见分类模型优缺点 决策树模型 决策树(Decision Tree)是一类常见的机器学习方法,可应用于分类与回归任务,这里主要讨论分类决策树。决策树是基于树结构来进行决策的。下图是使用决策树来决定…...
LaTeX符号大全:打破排版的边界
LaTeX符号大全:打破排版的边界 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,让我们一起探索一门极富表现力的排版艺术——LaTeX&…...
vue3-11
后端Java代码 src\router\a6router.ts文件 import { createRouter, createWebHashHistory } from vue-router import { useStorage } from vueuse/core import { Menu, Route } from ../model/Model8080 const clientRoutes [{path: /login,name: login,component: () > …...
【c语言】飞机大战2
1.优化边界问题 之前视频中当使用drawAlpha函数时,是为了去除飞机后面变透明,当时当飞机到达边界的时候,会出现异常退出,这是因为drawAlpha函数不稳定,昨天试过制作掩码图,下载了一个ps,改的话,…...
海康visionmaster-渲染控件:渲染控件加载本地图像的方法
描述 环境:VM4.0.0 VS2015 及以上 现象:渲染控件如何显示本地图像? 解答 思路:在 2.3.1 中,可以通过绑定流程或者模块来显示图像和渲染效果。因此,第一步, 可以使用在 VM 软件平台中给图像源模…...
【SD】一致性角色 - 同一人物 不同姿势 - 2
首先生成4张不同姿势的图片 masterpiece,high quality,(white background:1.6),(simple background:1.4),1gril,solo,black footwear,black hair,brown eyes,closed mouth,full body,glasses,jacket,long hair,long sleeves,lookig at viewer,plaid,plaid skirt,pleated shirt,…...
摩尔线程S80对于软件的支持
摩尔线程对软件的支持 时间:2024年1月1日 显卡型号:MTT S80 主板型号:七彩虹 igame z590 火神 V20 CPU: intel core i5 10400f 内存: 海盗船3600 16*2 存储: 致态1Tb nvme 显卡的驱动是最新的。 游戏 S…...
基数排序 RadixSort
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数 . 动态演示 :…...
Maven下载和安装的详细教程
文章目录 一、Maven下载和安装1.1 下载 Maven1.2 配置环境变量 参考资料 一、Maven下载和安装 1.1 下载 Maven 打开 Maven 的官方网站Maven – Download Apache Maven,下载最新版本的 Maven 在可选择的版本中,不同版本的区别在于: binary是已经编译过的…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
