网站首页 > 博客文章 正文
(五)原子操作
原子操作(Atomic Operations)是一种在并发编程中用于防止数据竞争和保证线程安全性的机制。原子操作是不可中断的操作,一旦开始,就必须完全执行完毕,不受其他线程干扰,可以确保多个线程在访问和修改共享数据时不会发生冲突,从而避免了数据竞争和相关的并发问题。
std::atomic是C++11标准库引入的一个模板类,它提供了对基本数据类型(如int、bool、float,指针等)的原子操作支持。
C++中的原子操作
在C++中,std::atomic类型提供了一系列操作保证了操作的原子性,即操作在执行过程中不会被其他线程打断。
- 构造和赋值:
- 构造函数:可以用初始值构造std::atomic对象。
- 赋值操作符:= 可以用于给std::atomic对象赋值,但这不是原子操作。
- 存储和加载:
- store(T desr, std::memory_order order = std::memory_order_seq_cst):原子地将指定的值存储到原子对象中。
- load(std::memory_order order = std::memory_order_seq_cst) const:原子地从原子对象中加载值。
- 算术操作:
- 对于整数类型的std::atomic,提供了 ++、--、+=、-=、*=、/=、&=、|=、^= 等算术操作符的重载,它们都是原子的。
- fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst): 原子地增加arg
- fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) : 原子地减少arg
- 比较并交换:
- compare_exchange_weak(T& expected, T desired, std::memory_order success = std::memory_order_seq_cst, std::memory_order failure = std::memory_order_seq_cst)
- compare_exchange_strong(T& expected, T desired, std::memory_order success = std::memory_order_seq_cst, std::memory_order failure = std::memory_order_seq_cst)
这两个函数尝试用desired替换当前值,但仅当当前值等于expected时才这样做。如果替换成功,expected将被更新为新的值。
两者的主要区别在于 compare_exchange_weak 可能会在期望值和当前值匹配时因伪失败而返回 false,而 compare_exchange_strong 则保证在匹配时一定返回 true 并执行交换。
伪失败指的是某些操作在逻辑上应该成功但由于某些原因(如硬件或操作系统的优化)而未能成功。
- 交换:
- exchange(T desired, std::memory_order order = std::memory_order_seq_cst):原子地将desired与当前值交换,并返回旧值。
- 位操作:
- 对于整数类型的std::atomic,也提供了位操作符的重载,如&=、|=、^=等。
- 非成员函数:
- std::atomic_flag 是一个特殊的原子类型,它只提供了一个操作:test_and_set,用于实现自旋锁等低级同步原语。
内存顺序
上述许多操作都接受一个std::memory_order参数来指定内存顺序(Memory Order)。内存顺序决定了两个或多个内存访问操作之间的相对顺序。在多线程环境中,由于处理器缓存、指令重排和编译器优化等因素,内存访问的顺序可能并不总是按照代码中的顺序执行。这可能导致数据竞争(data race)和未定义行为(undefined behavior)。在C++11及以后的版本中,std::atomic和std::memory_order一起提供了对内存顺序的细粒度控制。 std::memory_order 枚举类型定义了不同的内存顺序约束,这些约束用于控制编译器和处理器对内存操作的重新排序。不同的内存顺序约束有不同的性能和正确性保证。常见的内存顺序包括:
- std::memory_order_relaxed:最宽松的约束,不保证任何特定的内存顺序。适用于不需要同步或顺序保证的原子操作,如统计计数器的增加。
- std::memory_order_consume:主要应用于消费者-生产者模型中的依赖关系同步,确保依赖于原子操作结果的后续操作能按正确顺序执行。
- std::memory_order_acquire:在读取原子变量之前使用,确保之前的所有读取和写入操作对当前线程可见,常用于读-写锁(read-write lock)的读操作。
- std::memory_order_release:在写入原子变量之后使用,确保之后的所有读取和写入操作对其他线程可见,常用于写-写锁(write-write lock)的写操作或发布新值到共享变量。
- std::memory_order_acq_rel:结合了acquire和release的语义,常用于保护一段代码(critical section)的起始和结束,确保只有单一线程能执行这段代码。
- std::memory_order_seq_cst:提供最强的顺序一致性保证,保证所有线程看到的操作顺序都是一致的,就像单线程执行一样。适用于需要严格顺序保证的场景。
原子操作解决数据竞争问题
【C++并发编程】(三)互斥锁:std::mutex 使用互斥锁解决的数据竞争问题,原子操作也可以解决。下面给出一个例子:
#include <iostream>
#include <thread>
std::atomic<int> counter(0); // 定义一个原子整数counter
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 原子自增操作
}
}
int main() {
std::thread th1(increment);
std::thread th2(increment);
th1.join();
th2.join();
std::cout << "Final counter value: " << counter << std::endl;
// Final counter value: 200000
return 0;
}
由于counter是std::atomic类型的,所以++counter操作是原子的,即使在多线程环境下也不会产生数据竞争。
原子操作与互斥锁
原子操作通常比互斥锁具有更高的性能,但原子操作通常针对单个数据项(如一个整数或指针)进行,它们确保了对该数据项的访问是原子的,即不会被其他线程打断。互斥锁则通常用于保护一个代码块或整个数据结构,确保在该代码块或数据结构被访问时,只有一个线程能够执行。因此,在选择使用原子操作还是互斥锁时,需要根据具体的应用场景和需求进行权衡。
猜你喜欢
- 2024-09-26 “全栈2019”Java原子操作第十一章:CAS与ABA问题介绍及解决方案
- 2024-09-26 【Go语言】Golang中原子操作用法(go 原子操作)
- 2024-09-26 从CAS讲起,真正高性能解决并发编程的原子操作
- 2024-09-26 “全栈2019”Java原子操作第九章:atomic包下原子数组介绍与使用
- 2024-09-26 “全栈2019”Java原子操作第九章:atomic包下原子数组简单介绍
- 2024-09-26 “全栈2019”Java原子操作第十一章:CAS与ABA问题介绍与探讨
- 2024-09-26 CAS原子操作实现无锁及性能分析(cas能保证原子性吗)
- 2024-09-26 面试必备:Java 原子操作的实现原理「精品长文」
- 2024-09-26 java的原子操作和原子操作的好处(java原子类的使用场景)
- 2024-09-26 原子操作&普通锁&读写锁原理讲解
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)