C++并发数据结构设计

news2024/12/25 22:20:18

关键词:原子操作,无锁设计

引入问题->   

为什么需要原子操作->

原子操作实现以及原理->

c++原子操作接口->

c++基于原子操作的数据结构设计->

原子操作

什么是原子操作

  所谓原子操作,就是"不可中断的一个或一系列操作" 。

2.    硬件级的原子操作

在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。

        在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。

3.    软件级的原子操作

  软件级的原子操作实现依赖于硬件原子操作的支持。 对于linux而言,内核提供了两组原子操作接口:一组是针对整数进行操作;另一组是针对单独的位进行操作。

linux内核原子操作

C++原子操作接口

原子指令用于在多个CPU之间维护同步关系。在一些科学计算问题中,通过并行算法把子问题分配到多个cpu上执行,但是各个子问题之间存在合作关系,因此需要硬件机制来实现多个cpu之间同步。

每个 std::atomic 模板的实例化和全特化定义一个原子类型。若一个线程写入原子对象,同时另一线程从它读取,则行为良好定义(数据竞争的细节见内存模型)。

另外,对原子对象的访问可以建立线程间同步,并按 std::memory_order (内存次序)所对非原子内存访问定序。

std::atomic 既不可复制亦不可移动。

还有一些其他的类型别名,可以参见std::atomic。

他们全部具备成员函数is_lock_free(),准许使用者判定某一给定的类型上的操作是由原子指令(atomic instruction)直接实现(x.is_lock_free()返回true),还是要借助编译器和程序库的内部锁来实现(x.is_lock_free()返回false)。

成员函数

is_lock_free

检查原子对象是否免锁

(公开成员函数)

store

原子地以非原子对象替换原子对象的值

(公开成员函数)

load

原子地获得原子对象的值

(公开成员函数)

exchange

原子地替换原子对象的值并获得它先前持有的值

(公开成员函数)

compare_exchange_weak

compare_exchange_strong

原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载

(公开成员函数)

比较-交换

这一操作被称为”比较-交换“(compare-exchange),实现形式是成员函数compare_exchange_weak()和compare_exchange_strong()。

使用者给定一个期望值,原子变量将它和自身的值做比较,如果相等,就传入另一个既定的值;否则,更新期望值所属的变量,向它赋予原子变量的值。

比较交换函数返回布尔类型,如果完成了保存动作(前提是两值相等),则操作成功,函数返回ture,反之操作失败,函数返回false。

compare_exchange_weak()

对于compare_exchange_weak(),即使原子变量的值等于期望值,保存动作还是有可能失败,在这种情形下,原子变量维持不变,compare_exchange_weak()返回false;

原子化的比较-交换必须由一条指令单独完成,而某些处理器没有这种指令,无法保证该操作按原子化方式完成。

要实现比较-交换,负责的线程则必须改为连续运行一些列指令,但在这些计算机上,只要出现线程数量多于处理器数量的情形,线程就有可能执行到中途因系统调度而切出,导致操作失败。

这种计算机最有可能引发上述的保存失败,我们称之为佯败(spurious failure)。其因败因不算变量值本身存在问题,而是函数执行时机不对。

因为compare_exchange_weak()可能佯败,所以它必须配合循环使用。

#include <iostream>
#include <atomic>

using namespace std;

int main()
{
	bool expected = false;
	extern atomic<bool> b; //由其他源文件的代码设定变量的值
	
	while(!b.compare_exchange_weak(expected, true) && !expected);
	{
		cout << "b=" << b << endl;
	}

	return 0;
}

atomic<bool> b;

compare_exchange_strong()

只有当原子变量的值不符合预期时,compare_exchange_strong()才返回false。

这让我们得以明确知悉变量是否成功修改,或者是否存在另一线程抢先切入而导致佯败,从而能够摆脱上例所示的循环。

compare_exchange_weak()和compare_exchange_strong()使用场景对比

  • 在某些平台上,虽然使用compare_exchange_weak()可能导致佯败,但改用compare_exchange_strong()却会形成双重循环(因为compare_exchange_strong()自身内部含有一个循环),那么采用compare_exchange_weak()比较有利于性能。

  • 反之,如果存入的值需要耗时的计算,选择compare_exchange_strong()则更加合理。因为只要预期值没有变化,就可避免重复计算。

特化成员函数

fetch_add

原子地将参数加到存储于原子对象的值,并返回先前保有的值

(公开成员函数)

fetch_sub

原子地从存储于原子对象的值减去参数,并获得先前保有的值

(公开成员函数)

fetch_and

原子地进行参数和原子对象的值的逐位与,并获得先前保有的值

(公开成员函数)

fetch_or

原子地进行参数和原子对象的值的逐位或,并获得先前保有的值

(公开成员函数)

fetch_xor

原子地进行参数和原子对象的值的逐位异或,并获得先前保有的值

(公开成员函数)

operator++

operator++(int)

operator--

operator--(int)

令原子值增加或减少一

(公开成员函数)

operator+=

operator-=

operator&=

operator|=

operator^=

加、减,或与原子值进行逐位与、或、异或

std:atomic<T*>

 std::atomic 模板可用任何满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssignable) 的可平凡复制 (TriviallyCopyable) 类型 T 特化。

#include <iostream>
#include <atomic>
#include<cassert>

using namespace std;

class foo {};


int main()
{
	foo foo_array[5];

	// 可以和boo类型类比,定义一个foo*指针,初始值是数组的第一个对象。
	atomic<foo*> p(foo_array);
	foo* x = p.fetch_add(2);	// 令p+2,返回旧址。

	assert(x==foo_array);
	assert(p.load() == &foo_array[2]);

	x = (p -= 1);     //令p-1,返回新值
	assert(x == &foo_array[1]);
	assert(p.load() == &foo_array[1]);

	return 0;
}

无锁数据结构-无锁队列

待补充!

参考文档:

C++之原子操作(atomic)

C++之原子操作(atomic)_c++ 原子操作_小谢%同学的博客-CSDN博客

【并发编程十三】c++原子操作(1)

https://zhengjunxue.blog.csdn.net/article/details/128727418

C++ 参考手册

C++ 参考手册 - C++中文 - API参考文档

C++ 11 多线程初探-std::memory_order

https://www.cnblogs.com/lizhanzhe/p/10893016.html

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

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

相关文章

列表和元组(上)——“Python”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是Python中的列表&#xff0c;下面&#xff0c;让我们进入列表的世界吧 列表是什么, 元组是什么 创建列表 访问下标 切片操作 遍历列表元素 列表是什么, 元组是什么 编程中, 经常需要使用变量, 来保存/表示数据. 如果…

Swift 注释和文档

今天&#xff0c;我知道我写是什么&#xff0c;上帝和我知道 明天&#xff0c;我知道这个代码什么意思&#xff0c; 后天&#xff0c;我知道这是我写的代码&#xff0c; 一周后&#xff0c;这TM谁写的代码&#xff0c;此时只有上帝才知道啥意思 论代码注释的重要性。 普通…

React Native 混合ios android开发 及常用框架

英文文档&#xff1a;Setting up the development environment React Native 中文文档&#xff1a;集成到现有原生应用 React Native 中文网 ios 在集成过程中&#xff0c;需要修改package.json 和 Podfile&#xff0c;按文档中的内容&#xff0c;如果pod install过不了的…

用Kotlin 一步步抄作业写一个Redux

前言 我抄的作业 完全理解 redux&#xff08;从零实现一个 redux&#xff09; Issue #22 brickspert/blog GitHub 一.架构设计模式 1. mvc>mvp>mvvm->mvi 2.Redux实现类Mvi Android Mvi 与Redux对比&#xff0c;思想一致&#xff0c;单向数据流&#xff0c;单…

最优控制 3:最优控制理论中的极小值原理与动态规划

最优控制 3&#xff1a;使用极小值原理求解最优控制问题 引言极小值原理 t f t_f tf​ 固定的情况 t f t_f tf​ 自由的情况 动态规划连续系统 HJB 方程的推导 引言 经典变分法是一种特别强大的工具&#xff0c;但是它要求控制量必须可导且无界&#xff0c;这在很多问题中都是…

pandas读取列数不同的CSV文件

使用pandas读取每行不同列的CSV文件 对于序列模型而言&#xff0c;每条数据的大小都不一定相等&#xff0c;但对于一般的神经网络要求输入大小相等。目前的一种方法是选取当前数据集中最大长度的数据作为基准数据大小&#xff0c;其余的数据末尾补零来规范整个数据集每条数据的…

计算机:理解操作系统:内存篇(下)

内存 1. 指针与引用2. 进程的内存模型3. 幻象大师---操作系统4. 总结 本篇是 关于计算机内存最后一篇文章 什么是内存 C/C内存模型 堆区与栈区的本质 Java、Python等内存模型 Java内存模型 Jave中的堆区与栈区是如何实现的 Python内存模型 指针与引用 进程的内存模型 幻想大师-…

css tooltip (web.dev)

目录 版权介绍tool-tip在上居中动画效果宽度边界 tool-tip::after范围锥形渐变-webkit-mask尖角怎么来的? 附录完整代码 版权 本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/130262213. 介绍 https://web.dev…

【周末闲谈】AI作图,你真的了解它吗?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️周末闲谈】 系列目录 ✨第一周 二进制VS三进制 ✨第二周 文心一言&#xff0c;模仿还是超越&#xff1f; ✨第二周 畅想AR 文章目录 系列目录前言AI绘画&#x1f916;&#x1f916;&#x1f916;工作…

[C++]:万字超详细讲解多态以及多态的实现原理(面试的必考的c++考点)

文章目录 前言一、多态的定义及实现1.多态的构成条件2.c11的override和final3.重载&#xff0c;重写&#xff0c;重定义的比较4.抽象类5.多态的原理6.多继承中的虚函数表7.动态绑定和静态绑定总结 前言 多态的概念&#xff1a; 多态的概念&#xff1a;通俗来说&#xff0c;就是…

【Linux】使用systemd设置开机自启动命令

目录 1 使用使用systemd实现开机自动运行命令1.1 新建一个.service文件1.2 编写.service文件1.2.1 [Unit]1.2.2 [Service]1.2.3 [Install] 1.3 启动服务并设置自启动 2 编写Systemd服务文件的要点2.1 Systemd服务文件的位置2.2 Systemd服务文件的格式2.3 Systemd服务文件的基本…

【基础】Kafka -- 基础架构及核心概念

Kafka -- 基础架构及核心概念 初识 KafkaKafka 基本架构Kafka 主题与分区主题与分区分区副本机制 Replica高水位 HW 生产者生产者客户端必要的参数配置消息的发送序列化分区器生产者拦截器 原理分析重要的生产者参数 消费者消费者与消费者组消费者客户端必要的参数配置订阅主题…

MySQL 按关键字进行截取

一、问题背景 取MySQL数据表中某个字段中的IP信息。 如&#xff1a;t_log 表中的 user_ip 字段值为 {“username”:“miracle”,“ip”:“110.230.128.186”}&#xff0c;取出IP信息 110.230.128.186。 建表和初始化SQL语句&#xff0c;如下&#xff1a; SET NAMES utf8mb4…

GORM操作mysql数据库

对象就是程序的数据结构&#xff0c;关系是数据库。就是将程序的数据结构与数据库表对应起来。 在GORM是Go语言的ORM框架&#xff0c;将go的数据结构转化为数据库表&#xff0c;例如将结构体转化为数据库表。 引入gorm框架 远程下载gorm框架 go get -u gorm.io/driver/mysq…

HTTP中的Content-type详解

Content-Type Content-Type&#xff08;MediaType&#xff09;&#xff0c;即是Internet Media Type&#xff0c;互联网媒体类型&#xff0c;也叫做MIME类型。在互联网中有成百上千中不同的数据类型&#xff0c;HTTP在传输数据对象时会为他们打上称为MIME的数据格式标签&#x…

关于java.io的学习记录(写入文本)

可以通过字节流&#xff08;FileOutputStream&#xff09;、字符流&#xff08;OutputStreamWriter&#xff09;、字符缓冲流&#xff08;BufferedWriter&#xff09;读取文本中的数据。 FileOutputStream写入文本 public void write(){String path "writeTest.txt"…

【是C++,不是C艹】 什么是C++ | C++从哪来 | 学习建议

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e; &#x1f449;专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 我知道你急着学C&#xff0c;但你先别急&#xff0c;薛之谦认识认识C还是很有必要的。本期跟大家聊…

文件夹改名,如何在改名之后批量复制文件夹名称

在日常时候中会遇到给文件夹改名的时候&#xff0c;那么我们又如何在改名之后批量复制文件夹名称&#xff1f;今天就由小编来给大家分享一下操作办法。 首先第一步&#xff0c;我们要进入文件批量改名高手&#xff0c;并在板块栏里选择“文件夹批量改名”板块。 第二步&#xf…

SpringBoot 接入chatGPT API

SpringBoot 接入chatGPT API 一、准备工作二、补全接口示例三、申请API-KEY**四、JavaScript调用API**五、SpringBoot整合ChatGPT六、使用curl模拟请求ChatGPT平台已经为技术提供了一个入口了,作为一个Java程序员,我们第一时间想到的就是快速开发一个应用,接入ChatGPT的接口…

第十四天本地锁、Redis分布锁、Redisson锁三者的区别

一、为什么要有redis分布式锁&#xff0c;它解决了什么问题&#xff1f; 在传统单体架构的项目下&#xff0c;使用本地锁synchronized和lock锁就可以锁住当前进程&#xff0c;保证线程的安全性&#xff0c;但是本地锁解决不了分布式环境下多个服务资源共享的问题&#xff0c;而…