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是已经编译过的…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...