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_boolbool
atomic_charchar
atomic_scharsigned char
atomic_ucharunsigned char
atomic_intint
atomic_uintunsigned int
atomic_shortshort
atomic_ushortunsigned short
atomic_longlong
atomic_ulongunsigned long
atomic_llonglong long
atomic_ullongunsigned long long
atomic_char16_tchar16_t
atomic_char32_tchar32_t
atomic_wchar_twchar_t

(3).另一种使用原子类型的方式
使用std::atomic<T>模板类。
注意点:
a.该模板类不支持拷贝构造,移动构造,赋值运算符。
b.std::atomic<T>定义了到T的类型转换函数。

(4).atomic类型及其相关的操作

操作atomic_flagatomic_boolatomic_integral_typeatomic<bool>atomic<T*>atomic<integral-type>Atomic<class-type>
test_and_setY
clearY
is_lock_freeyyyyyy
loadyyyyyy
storeyyyyyy
exchangeyyyyyy
compare_exchange_weak+strongyyyyyy
fetch_add,+=yyy
fetch_sub,-=yyy
fetch_or,|=yy
fetch_and,&=yy
fetch_xor,^=yy
++,--yyyy

(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,34,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_relmemory_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;
}

t1memory_order_release会保证a的写入先执行,再执行b的写入。
t2memory_order_acquire会保证先读取b,再读取a
上述两个限制下,我们知道t2a将会符合预期。

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

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

相关文章

力扣:209. 长度最小的子数组(Python3)

题目&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0 。 来源&#xff1…

模版匹配历劫之路1-匹配点太多如何解决

1测试图片 2初步推测是否是提取的点太多而导致匹配时间很长 2.1通过canny的算法来提取检测点 import numpy as np import cv2 import time import matplotlib.pyplot as pltclass GeoMatch:def __init__(self):self.noOfCordinates0 # 坐标数组中元素的个数self.cordinates…

思维链COT原理探究

要进行因果分析&#xff0c;需要把思维链中的不同元素拆解开来&#xff0c;然后通过控制变量实验&#xff0c;来研究不同元素对COT效果的影响。以下两篇论文的核心差异就在于: COT的变量拆解&#xff0c;以及控制变量的实验方式。 结合两篇论文的实验结论&#xff0c;可能导致…

MIT线性代数笔记-第34讲-左右逆,伪逆

目录 34.左右逆&#xff0c;伪逆左右逆伪逆 打赏 34.左右逆&#xff0c;伪逆 左右逆 之前讲到的逆都是针对可逆方阵而言的&#xff0c;对于长方矩阵&#xff0c;实际上也有广义的逆&#xff0c;那就是左逆和右逆 左逆 当矩阵列满秩&#xff0c;即 r n r n rn时&#xff0c;…

老子的《道德经》透露,不努力反而更成功

人类生而自由&#xff0c;但到处都是枷锁。 永远不要怀疑经过慎思且足够投入的一小群人能否改变这个世界。事实上&#xff0c;只有他们才办得到。 优美灵魂的两个发展方向&#xff1a;崇拜道德的天才&#xff0c;对别人实行道德的判断。 一、道 《道德经》开始的名字是《老子…

关键字:try-catch关键字

在 Java 中&#xff0c;try-catch关键字用于异常处理。它们允许编写代码来捕获和处理异常&#xff0c;以确保程序能够在出现问题时合理地处理它们而不会崩溃。 以下是try-catch关键字的基本语法&#xff1a; 在try块中编写可能会抛出异常的代码。如果在try块中的任何代码抛出…

全连接层:神经网络的桥梁

全连接层&#xff1a;神经网络的桥梁 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;让我们一同探讨深度学习中至关重要的一环——“全连接层”&am…

Linux上使用Certbot生成免费SSL证书

Linux上使用Certbot生成免费SSL证书 补充&#xff1a;certbot在大多数linux上是自带的&#xff0c;若没有可执行以下命令下载 Ubuntu&#xff1a; sudo apt-get update sudo apt-get install --only-upgrade certbotCentos sudo yum update sudo yum install certbot一、暂时停…

JavaScript setTimeout和setInterval的用法与区别详解

目录 I. 总述 II. setTimeout()函数 III. setInterval()函数 IV. 新年倒计时案例 Javascript的setTimeOut和setInterval函数应用非常广泛&#xff0c;它们都用来处理延时和定时任务&#xff0c;下面这篇文章主要给大家介绍了关于JavaScript setTimeout和setInterval的用法与…

读取某股票的日线数据

只需修改对应股票的ts_code&#xff0c;start_date&#xff0c;end_date即可获取对应股票的全部数据。 import tushare as ts import pandas as pdpd.set_option(expand_frame_repr, False) # 当列太多时不换行 pd.set_option(display.max_rows, 5000) # 最多显示数据的行数…

结构型设计模式—装饰器模式

装饰器模式&#xff1a;不改变原对象的基础上&#xff0c;灵活地给对象添加额外职责。装饰器相比于生成子类更加灵活。即将实现同一接口的父类当做参数传入包装器对象&#xff0c;动态创建出新的对象。 给对象添加新行为最简单直观的办法就是扩展本体对象&#xff0c;通过继承…

编程笔记 html5cssjs 018 HTML颜色

编程笔记 html5&css&js 018 HTML颜色 一、HTML 颜色二、HTML中设置颜色值注意 颜色是视觉中重要因素&#xff0c;尤其是处理人机界面中&#xff0c;更是要处理颜色设置和搭配。在网页中&#xff0c;提供了设置颜色的一些方案&#xff0c;需要我们认真学习和掌握。 一、…

HTML5和JS实现新年礼花效果

HTML5和JS实现新年礼花效果 2023兔年再见&#xff0c;2024龙年来临了&#xff01; 祝愿读者朋友们在2024年里&#xff0c;身体健康&#xff0c;心灵愉悦&#xff0c;梦想成真。 下面是用HTML5和JS实现新年礼花效果&#xff1a; 源码如下&#xff1a; <!DOCTYPE html>…

MySQL数据库学习一

1 什么是数据库的事务&#xff1f; 1.1 事务的典型场景 在项目里面&#xff0c;什么地方会开启事务&#xff0c;或者配置了事务&#xff1f;无论是在方法上加注解&#xff0c;还 是配置切面。 <tx:advice id"txAdvice" transaction-manager"transactionMa…

个人简历范本(精选5篇)

HR浏览一份简历也就25秒左右&#xff0c;如果你连「好简历」都没有&#xff0c;怎么能找到好工作呢&#xff1f; 如果你不懂得如何在简历上展示自己&#xff0c;或者觉得怎么改简历都不出彩&#xff0c;那请你一定仔细读完。 个人求职简历第 1 篇 男 22 本科 AI简历 市场营…

007、控制流

先看下本篇学习内容&#xff1a; 通过条件来执行 或 重复执行某些代码 是大部分编程语言的基础组成部分。在Rust中用来控制程序执行流的结构主要就是 if表达式 与 循环表达式。 1. if表达式 if表达式允许我们根据条件执行不同的代码分支。我们提供一个条件&#xff0c;并且做出…

【NTN 卫星通信】Oneweb星座以及Oneweb与Starlink比较

1 什么是OneWeb OneWeb于2012年以WorldVu的名义成立&#xff0c;于2020年开始构建其星座。然而&#xff0c;对于这家英国公司来说&#xff0c;这是一个艰难的旅程&#xff0c;OneWeb于2020年3月宣布破产&#xff0c;并认为covid-19大流行是一个主要因素。OneWeb星座当时仅完成…

一次降低进程IO延迟的性能优化实践——基于block层bfq调度器

如果有个进程正频繁的读写文件&#xff0c;此时你vim查看一个新文件&#xff0c;将会出现明显卡顿。即便你vim查看的文件只有几十M&#xff0c;也可能会出现卡顿。相对的&#xff0c;线上经常遇到IO敏感进程偶发IO超时问题。这些进程一次读写的文件数据量很少&#xff0c;正常几…

Redis的缓存过期淘汰策略

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

RocketMQ(Linux版本5.1.4)

1、停止之前的运行服务 [roottssvr1-c1 rocketmq-all-4.7.0-bin-release]# sh bin/mqshutdown namesrv No mqnamesrv running. [roottssvr1-c1 rocketmq-all-4.7.0-bin-release]# [roottssvr1-c1 rocketmq-all-4.7.0-bin-release]# [roottssvr1-c1 rocketmq-all-4.7.0-bin-r…