【C++11并发】何为std::atomic,其原理如何,以及使用注意事项
2021/4/9 22:25:55
本文主要是介绍【C++11并发】何为std::atomic,其原理如何,以及使用注意事项,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
对原子类型的某些操作不一定是线程安全的,比如说
a=a+12;
非线程安全
a+=12;
线程安全
分析如下
Cppreference介绍
成员函数(原子操作)
is_lock_free | |
---|---|
store | 用非原子参数替换原子对象的值 |
load | 获取原子对象的值 |
exchange | 交换两个原子对象的值 |
wait | 阻塞线程,直到收到通知并且原子值发生变化 |
notify_one | 通知至少一个线程在等待原子对象 |
notify_all | 通知所有阻塞的线程等待原子对象 |
特殊的成员函数(原子操作)
fetch_add | 将参数添加到存储在原子对象中的值中,并获取前面保存的值 |
---|---|
fetch_sub | 类似 |
fetch_and | 类似 |
fetch_or | 类似 |
fetch_xor | 类似 |
相关重载运算符(原子操作)
What exactly is std::atomic?
我知道std::atomic<>
是一个原子物体。但是原子能达到什么程度?据我所知,操作可以是原子的。使物体成为原子的确切含义是什么?例如,如果有两个线程同时执行以下代码:
a=a+12;
那么整个操作(比如add_twelve_to(int)
是原子的吗?还是对可变原子进行了更改?
ANSWER:
std::atomic<>
的每个实例化和完全特化表示一种类型,不同的线程可以同时操作(该类型实例),而不会引发未定义的行为:
原子类型的对象是唯一没有数据竞争的c++对象;也就是说,如果一个线程写一个原子对象,而另一个线程从它读取,这种行为是定义良好的。
此外,对原子对象的访问可以建立线程间同步,并按std::memory_order指定的顺序对非原子内存访问进行排序。
std::atomic<>
所包装的操作,在c++11前的时代,必须使用msvc的互锁函数或GCC内置的atomic bultins;
另外,std::atomic<>
通过允许指定同步和排序约束的各种内存顺序,为您提供了更多的控制。如果您想阅读更多关于C++ 11原子和内存模型的内容,这些链接可能是有用的:
- C++原子与内存排序
- 比较:在C++ 11中使用原子锁与互斥锁和RW锁的无锁编程
- C++ 11引入了标准化内存模型。这是什么意思?它会如何影响C++程序设计?
- C++ 11中的并发性
注意,对于典型的用例,可使用重载运算符或另一些集合
std::atomic<long> value(0); value++; //This is an atomic op value += 5; //And so is this
因为运算符语法不允许您指定内存顺序,故这些操作可配合 std::memory_order_seq_cst来操作,其为C++ 11中所有原子操作的默认顺序。它保证所有原子操作之间的顺序一致性(全局排序)。
然而,在某些情况下,可以使用更明确的形式:
std::atomic<long> value {0}; value.fetch_add(1, std::memory_order_relaxed); // Atomic, but there are no synchronization or ordering constraints value.fetch_add(5, std::memory_order_release); // Atomic, performs 'release' operation
现在,您的示例:
a=a+12;
执行顺序如下:
std::memory_order_seq_cst
默认在此处使用
- 调用
a.load();
12+a.load();
- 调用
a.store();
但若将示例改为a+=12
,则将为原子操作(因此+=
重载了),上述操作大致与如下相等
a.fetch_add(12,std::memory_order_seq_cst)
关于你的评论:
A regular
int
has atomic loads and stores. Whats the point of wrapping it with atomic<>?
你的声明只适用于为存储和/或加载提供原子性保证的架构,但存在架构不保证上述操作的原子性。通常要求必须对与字/双字对齐的地址执行操作才能使其为原子的。但使用std::atomic
,可使用与任何平台并保持操作的原子性。
/* 使用std::atomic允许如下代码 */ void* sharedData = nullptr; std::atomic<int> ready_flag = 0; // Thread 1 void produce() { sharedData = generateData(); ready_flag.store(1, std::memory_order_release); } // Thread 2 void consume() { while (ready_flag.load(std::memory_order_acquire) == 0) { std::this_thread::yield(); } assert(sharedData != nullptr); // will never trigger processData(sharedData); }
注意,断言条件将始终为真(因此永远不会触发),因此您可以始终确保,在while循环退出后数据已就绪。这是因为:
store()
的调用晚于sharedData
的赋值(假设赋值均有效),并且使用顺序std::memory_order_release
memory_order_release
具有这种内存顺序的store
操作执行释放操作:当前线程中的任何读或写操作都不能在store
之后重新排序。当前线程中的所有写操作在其他获得相同原子变量的线程中都可见
sharedData
在while
循环退出后使用,故在load()
后,其会返回一个非0值,load()
使用std::memory_order_acquire
std::memory_order_acquire
具有此内存顺序的load
在受影响的内存位置上执行获取操作:当前线程中的任何读或写操作都不能在load()
之前重新排序。释放相同原子变量的其他线程中的所有写操作都在当前线程中可见。
这使您能够精确地控制同步,并允许您显式地指定代码 可能/可能不
/将会/将不会
的行为。如果只保证原子性本身,这是不可能的。尤其是当涉及到非常有趣的同步模型时,比如 release-consume ordering.
这篇关于【C++11并发】何为std::atomic,其原理如何,以及使用注意事项的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享