《C++中的原子操作:实现高效并发编程的关键》

在当今多线程编程的时代,数据的并发访问和修改是一个常见的问题。为了确保数据的一致性和正确性,传统的加锁机制被广泛使用。然而,锁的使用可能会导致性能瓶颈和死锁等问题。C++中的原子操作提供了一种更高效、更简洁的方式来处理并发数据访问,本文将深入探讨 C++中如何使用原子操作来实现高效的并发编程。

一、原子操作的概念和优势

原子操作是指不可分割的操作,即在执行过程中不会被其他线程中断。在多线程环境下,原子操作可以确保数据的一致性和正确性,而无需使用传统的锁机制。相比之下,原子操作具有以下优势:

1. 更高的性能:原子操作通常比锁机制更快,因为它们不需要进行上下文切换和等待锁的释放。

2. 避免死锁:使用锁机制可能会导致死锁问题,而原子操作不会出现死锁。
3. 更简洁的代码:原子操作可以使代码更加简洁和易于理解,避免了复杂的锁管理代码。

二、C++中的原子类型

C++标准库提供了一系列原子类型,包括  std::atomic 、 std::atomic 、 std::atomic  等。这些原子类型提供了原子操作的接口,可以用于实现并发数据访问的同步。

例如,下面的代码演示了如何使用  std::atomic  来实现一个简单的线程安全的标志:

cpp
复制
#include
#include
#include

std::atomic flag(false);

void set_flag() {
flag.store(true);
}

void check_flag() {
while (!flag.load()) {
// 等待标志被设置
}
std::cout << “Flag is set!” << std::endl;
}

int main() {
std::thread t1(set_flag);
std::thread t2(check_flag);

t1.join();
t2.join();return 0;

}

在上面的代码中, std::atomic  类型的  flag  变量用于表示一个标志。 set_flag  函数用于设置标志, check_flag  函数用于检查标志是否被设置。在  check_flag  函数中,使用  while (!flag.load())  循环来等待标志被设置。 flag.load()  函数用于读取标志的值,它是一个原子操作,确保在读取过程中不会被其他线程中断。

三、原子操作的方法

C++中的原子类型提供了一系列原子操作的方法,包括  load 、 store 、 exchange 、 compare_exchange_weak  和  compare_exchange_strong  等。这些方法可以用于读取、写入、交换和比较交换原子变量的值。

1.  load  和  store  方法

  • load  方法用于读取原子变量的值,它是一个原子操作,确保在读取过程中不会被其他线程中断。

  • store  方法用于写入原子变量的值,它也是一个原子操作,确保在写入过程中不会被其他线程中断。

例如,下面的代码演示了如何使用  load  和  store  方法来读取和写入原子变量的值:

cpp
复制
#include
#include
#include

std::atomic counter(0);

void increment_counter() {
for (int i = 0; i < 1000; ++i) {
counter.store(counter.load() + 1);
}
}

int main() {
std::thread t1(increment_counter);
std::thread t2(increment_counter);

t1.join();
t2.join();std::cout << "Counter value: " << counter.load() << std::endl;return 0;

}

在上面的代码中, std::atomic  类型的  counter  变量用于表示一个计数器。 increment_counter  函数用于增加计数器的值,它使用
 counter.store(counter.load() + 1)  语句来读取和写入计数器的值。由于  counter  是一个原子变量,所以在多线程环境下,计数器的值是正确的。

2.  exchange  方法

  • exchange  方法用于交换原子变量的值,并返回原子变量的旧值。它是一个原子操作,确保在交换过程中不会被其他线程中断。

例如,下面的代码演示了如何使用  exchange  方法来交换原子变量的值:

cpp
复制
#include
#include
#include

std::atomic value(0);

void set_value() {
value.exchange(10);
}

void get_value() {
int old_value = value.exchange(20);
std::cout << "Old value: " << old_value << std::endl;
}

int main() {
std::thread t1(set_value);
std::thread t2(get_value);

t1.join();
t2.join();std::cout << "Current value: " << value.load() << std::endl;return 0;

}

在上面的代码中, std::atomic  类型的  value  变量用于表示一个值。 set_value  函数用于设置值为 10, get_value  函数用于获取值并将其设置为 20。在  get_value  函数中,使用  int old_value = value.exchange(20)  语句来交换值并返回旧值。由于  value  是一个原子变量,所以在多线程环境下,交换操作是正确的。

3.  compare_exchange_weak  和  compare_exchange_strong  方法

  • compare_exchange_weak  和  compare_exchange_strong  方法用于比较交换原子变量的值。它们是原子操作,确保在比较交换过程中不会被其他线程中断。

  • compare_exchange_weak  方法可能会因为硬件原因而失败,即使原子变量的值与预期值相等。在这种情况下,它会返回  false ,并且不会修改原子变量的值。

  • compare_exchange_strong  方法不会因为硬件原因而失败,它会一直尝试直到成功为止。

例如,下面的代码演示了如何使用  compare_exchange_weak  和  compare_exchange_strong  方法来比较交换原子变量的值:

cpp
复制
#include
#include
#include

std::atomic value(0);

void compare_exchange() {
int expected = 0;
bool success = false;
while (!success) {
success = value.compare_exchange_weak(expected, 10);
}
std::cout << “Value is set to 10!” << std::endl;
}

void get_value() {
std::cout << "Current value: " << value.load() << std::endl;
}

int main() {
std::thread t1(compare_exchange);
std::thread t2(get_value);

t1.join();
t2.join();return 0;

}

在上面的代码中, std::atomic  类型的  value  变量用于表示一个值。 compare_exchange  函数用于比较交换值为 10,如果值为 0,则将其设置为 10。在比较交换过程中,使用  while (!success) { success = value.compare_exchange_weak(expected, 10); }  循环来确保比较交换成功。由于  value  是一个原子变量,所以在多线程环境下,比较交换操作是正确的。

四、原子操作的应用场景

原子操作在多线程编程中有广泛的应用场景,包括但不限于以下几个方面:

1. 计数器:原子操作可以用于实现线程安全的计数器,避免使用锁机制带来的性能瓶颈和死锁问题。

2. 标志:原子操作可以用于实现线程安全的标志,用于表示某个条件是否满足。

3. 资源管理:原子操作可以用于实现线程安全的资源管理,例如互斥锁、信号量等。

4. 并发数据结构:原子操作可以用于实现并发数据结构,例如并发队列、并发栈等。

五、注意事项

在使用原子操作时,需要注意以下几点:

1. 原子操作并不是万能的,它们不能替代所有的锁机制。在某些情况下,锁机制可能更加适合,例如需要长时间持有锁的情况。

2. 原子操作的性能取决于硬件和编译器的实现。在某些情况下,原子操作可能会比锁机制更慢,因此需要进行性能测试和优化。

3. 原子操作的正确性取决于程序员的正确使用。在使用原子操作时,需要确保操作的顺序和逻辑是正确的,避免出现数据竞争和不一致的情况。

结论

C++中的原子操作提供了一种高效、简洁的方式来处理并发数据访问。通过使用原子操作,我们可以避免使用传统的锁机制带来的性能瓶颈和死锁问题,提高程序的性能和可维护性。在实际的多线程编程中,我们应该根据具体的需求和场景,选择合适的原子操作和锁机制,以实现高效的并发编程。

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

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

相关文章

MySQL --表的约束

文章目录 1.空属性2.默认值3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键9.综合案例-阅读 1.空属性 两个值&#xff1a;null&#xff08;默认的&#xff09;和not null(不为空) 数据库默认字段基本都是字段为空&#xff0c;但是实际开发时&#xff0c;尽可能保证字段不为空…

AI客服对决:智能客服PK赛揭示企业级AI应用未来

如今的AI对抗场景&#xff0c;简直成了颇具娱乐性的“观赏项目”。围观群众们不仅看得津津有味&#xff0c;时不时还会发出阵阵笑声。 最近&#xff0c;一场别开生面的AI客服挑战赛引发了热议&#xff1a;一位使用“花开富贵”阿姨人设的Agent甩出一条链接&#xff0c;结果对面…

如何在命令执行超时时自动终止该命令

问题 问题 《Command line command to auto-kill a command after a certain amount of time》 中的回答提出了一种从 bash 命令行中为长时间运行的命令设置超时的方法&#xff1a; ( /path/to/slow command with options ) & sleep 5 ; kill $!但是有可能某个“耗时长”…

python绘制弦图-科研作图

一、背景 弦图以其直观、精美的展示方式受到越来越多人的关注&#xff0c;它不仅能够有效展示两个变量之间的联系&#xff0c;还能同时展现多个变量间的复杂互动&#xff0c;本文将通过Python语言中的pycirclize库&#xff0c;带你深入了解如何绘制弦图。 弦图是一种圆…

CQRS模型解析

简介 CQRS中文意思为命令于查询职责分离&#xff0c;我们可以将其了解成读写分离的思想。分为两个部分 业务侧和数据侧&#xff0c;业务侧主要执行的就是数据的写操作&#xff0c;而数据侧主要执行的就是数据的读操作。当然两侧的数据库可以是不同的。目前最为常用的CQRS思想方…

强大的重命名工具 | Bulk Rename Utility v4.0 便携版

软件简介 Bulk Rename Utility是一款功能强大且易于使用的文件批量重命名工具。它不仅体积小巧&#xff0c;而且完全免费&#xff0c;提供了友好的用户界面。该软件允许用户对文件或文件夹进行批量重命名&#xff0c;支持递归操作&#xff0c;即包含子文件夹的重命名。 软件特…

镜像导入、标签设置与应用、探针测试

一、镜像导入 1、master主机下载镜像并打包 [rootk8s-master ~]# docker pull nginx:1.20.0[rootk8s-master ~]# docker pull nginx:1.21.0[rootk8s-master ~]# docker pull nginx:1.25.0[rootk8s-master ~]# docker pull busybox:latest[rootk8s-master ~]# docker save -o n…

FreeRTOS下UART的封装

FreeRTOS下UART的封装_哔哩哔哩_bilibili Git使用的一个BUG&#xff1a; 当出现这个问题是因为git本身的安全证书路径有问题&#xff0c;我们需要重新指定路径 P1:UART程序层次

蓝桥杯嵌入式的学习总结

一. 前言 嵌入式竞赛实训平台(CT117E-M4) 是北京国信长天科技有限公司设计&#xff0c;生产的一款 “ 蓝桥杯全国软件与信息技术专业人才大赛-嵌入式设计与开发科目 “ 专用竞赛平台&#xff0c;平台以STM32G431RBT6为主控芯片&#xff0c;预留扩展板接口&#xff0c;可为用户提…

正点原子RK3588(二)——lenet测试和modelzoo模型

文章目录 一、lenet二、modelzoo模型2.1 介绍2.2 测试 一、lenet import cv2 import numpy as np from rknnlite.api import RKNNLite RKNN_MODEL LeNet5_mnist_model.rknndef show_top5(result):output result[0].reshape(-1)output_sorted sorted(output, reverseTrue)to…

AI资深导师指导-ChatGPT深度科研工作应用、论文撰写、数据分析及机器学习与AI绘图

2022年11月30日&#xff0c;可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT3.5&#xff0c;将人工智能的发展推向了一个新的高度。2023年4月&#xff0c;更强版本的ChatGPT4.0上线&#xff0c;文本、语音、图像等多模态交互方式使其在…

Fyne ( go跨平台GUI )中文文档-绘图和动画(三)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…

GAMES101(15节,辐射,BRDF)

Irradiance辐射度量学 辐射度量学在渲染领域&#xff0c;可以帮助理解基于物理的光照模型 radiant energy辐射能量Q&#xff0c;累计总能量&#xff08;单位J joule焦耳&#xff09;&#xff0c;就像太阳能板&#xff0c;光照时间越长接收能量越多&#xff0c;收到的能量总和…

python是什么语言写的

Python是一种计算机程序设计语言。是一种面向对象的动态类型语言。现今Python语言很火&#xff0c;可有人提问&#xff0c;这么火的语言它的底层又是什么语言编写的呢&#xff1f; python是C语言编写的&#xff0c;它有很多包也是用C语言写的。 所以说&#xff0c;C语言还是很…

解密的军事卫星图像在各种民用地理空间研究中都有应用

一、美军光学侦察卫星计划概述 国家侦察局 &#xff08;NRO&#xff09; 负责开发和操作太空侦察系统&#xff0c;并为美国国家安全开展情报相关活动。NRO 开发了几代机密锁眼 &#xff08;KH&#xff09; 军事光学侦察卫星&#xff0c;这些卫星一直是美国国防部 &#xff08;D…

Upload-Labs 文件上传靶场 通关

Pass-01 (本pass在客户端使用js对不合法图片进行检查&#xff01;) Pass-02 Pass-02 (本pass在服务端对数据包的MIME进行检查!) Pass-03 Pass-03 (本pass禁止上传.asp|.aspx|.php|.jsp后缀文件&#xff01;) AddType application/x-httpd-php .php .phtml .php3 .php4 .p…

Java-Part 0

Advanced Java and Cutting-edge Applications Part 0: Course presentation Part 1 其实就是个括号匹配问题&#xff0c;Stack 经典问题&#xff0c;但是好久没用Java&#xff0c;有一点点生疏&#xff0c;感觉老师的版本要简洁的多 package tiei.ajp.test;import java.uti…

java项目之基于spring boot的多维分类的知识管理系统的设计与实现源码

项目简介 基于spring boot的多维分类的知识管理系统的设计与实现实现了以下功能&#xff1a; 基于spring boot的多维分类的知识管理系统的设计与实现的主要使用者管理员可以管理用户信息&#xff0c;知识分类&#xff0c;知识信息等&#xff0c;用户可以查看和下载管理员发布…

Python|OpenCV-实现识别目标图像中的圆圈(20)

前言 本文是该专栏的第22篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 在处理图像检测项目的时候,可能会遇到需要检测目标图像中的“圆圈”需求。笔者在这里举个例子,如下图所示: 在图中有一个篮球,但是我们要找的目标对象并不是篮球,而是篮球它本身的这个…

BUUCTF-MISC-数据包中的线索

下载题目文件&#xff0c;解压发现是一段流量包 使用Wireshark打开 首先过滤HTTP数据流 然后追踪HTTP数据流 通过追踪数据流可以发现 流7 当中有一段base64编码&#xff0c;我们尝试解码 base64基本特征 Base64编码只包含64个字符&#xff1a;大写字母&#xff08;A-Z&#x…