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是已经编译过的…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...