二手房网站排行,站长素材官网免费,深圳办公室装修装修公司,wordpress 漏洞工具原子变量
概述
C11提供了一个原子类型std::atomicT#xff0c;通过这个原子类型管理的内部变量就可以称之为原子变量#xff0c;我们可以给原子类型指定bool、char、int、long、指针等类型作为模板参数#xff08;不支持浮点类型和复合类型#xff09;。
…原子变量
概述
C11提供了一个原子类型std::atomicT通过这个原子类型管理的内部变量就可以称之为原子变量我们可以给原子类型指定bool、char、int、long、指针等类型作为模板参数不支持浮点类型和复合类型。
原子指的是一系列不可被CPU上下文交换的机器指令这些指令组合在一起就形成了原子操作。在多核CPU下当某个CPU核心开始运行原子操作时会先暂停其它CPU内核对内存的操作以保证原子操作不会被其它CPU内核所干扰。
由于原子操作是通过指令提供的支持因此它的性能相比锁和消息传递会好很多。相比较于锁而言原子类型不需要开发者处理加锁和释放锁的问题同时支持修改读取等操作还具备较高的并发性能几乎所有的语言都支持原子类型。
可以看出原子类型是无锁类型但是无锁不代表无需等待因为原子类型内部使用了CAS循环当大量的冲突发生时该等待还是得等待但是总归比锁要好。
C11内置了整形的原子变量这样就可以更方便的使用原子变量了。在多线程操作中使用原子变量之后就不需要再使用互斥量来保护该变量了用起来更简洁。因为对原子变量进行的操作只能是一个原子操作atomic operation**原子操作指的是不会被线程调度机制打断的操作这种操作一旦开始就一直运行到结束中间不会有任何的上下文切换。**多线程同时访问共享资源造成数据混乱的原因就是因为CPU的上下文切换导致的使用原子变量解决了这个问题因此互斥锁的使用也就不再需要了。
1.atomic类成员
类定义
// 定义于头文件 atomic
template class T
struct atomic;通过定义可得知在使用这个模板类的时候一定要指定模板类型。
构造函数
// ①
atomic() noexcept default;
// ②
constexpr atomic( T desired ) noexcept;
// ③
atomic( const atomic ) delete;构造函数①默认无参构造函数。构造函数②使用 desired 初始化原子变量的值。构造函数③使用delete显示删除拷贝构造函数, 不允许进行对象之间的拷贝
公共成员函数
原子类型在类内部重载了操作符并且不允许在类的外部使用 进行对象的拷贝
T operator( T desired ) noexcept;
T operator( T desired ) volatile noexcept;atomic operator( const atomic ) delete;
atomic operator( const atomic ) volatile delete;也就是说:
void test01() {atomic_int a 0; // correctatomic_int d(0); // correct// atomic_int b a; // error// atomic_int c(a); // error
}store函数
store 函数用于将一个值存储到原子变量中。它的基本形式如下
void store( T desired, std::memory_order order std::memory_order_seq_cst ) noexcept;
void store( T desired, std::memory_order order std::memory_order_seq_cst ) volatile noexcept;desired表示要存储到原子变量中的值。order表示存储操作的内存序(memory order)默认为 memory_order_seq_cst即顺序一致性。(可不指定)
load函数
load 函数用于从原子变量中加载当前的值。它的基本形式如下
T load(memory_order order memory_order_seq_cst) const noexcept;order表示加载操作的内存序(memory order)默认为 memory_order_seq_cst即顺序一致性。
两个函数示例:
#include iostream
using namespace std;
#include atomic
#include threadatomic_int atomicCount(0);void test02() {for (int i 0; i 100; i) {atomicCount.store(atomicCount.load() 1);}}int main() {thread t1(test02);thread t2(test02);t1.join();t2.join();cout atomicCount.load() endl; // 200
}在这个例子中两个线程并发地对 atomicCounter 进行递增操作。由于 store 和 load 都是原子操作因此可以确保对 atomicCounter 的操作是线程安全的。最后输出的 atomicCounter 的值是预期的 200。
特化成员函数
主要说的是赋值运算符重载: 以上各个 operator 都会有对应的 fetch_* 操作详细见下表 见代码:
void test01() {atomic_int a 0; // correctatomic_int d(1); // correct// atomic_int b a; // error// atomic_int c(a); // errora.store(10);a;a d;auto e a d;cout e endl; // 0
}内存顺序约束
也就是load函数和store函数的参数 memory_order, 以指定如何同步不同线程上的其他操作。
定义如下:
typedef enum memory_order {memory_order_relaxed, // relaxedmemory_order_consume, // consumememory_order_acquire, // acquirememory_order_release, // releasememory_order_acq_rel, // acquire/releasememory_order_seq_cst // sequentially consistent
} memory_order;memory_order_relaxed 这是最宽松的规则它对编译器和CPU不做任何限制可以乱序memory_order_release 释放设定内存屏障(Memory barrier)保证它之前的操作永远在它之前但是它后面的操作可能被重排到它前面memory_order_acquire 获取, 设定内存屏障保证在它之后的访问永远在它之后但是它之前的操作却有可能被重排到它后面往往和Release在不同线程中联合使用memory_order_consume改进版的memory_order_acquire 开销更小memory_order_acq_rel它是Acquire 和 Release 的结合同时拥有它们俩提供的保证。比如你要对一个 atomic 自增 1同时希望该操作之前和之后的读取或写入操作不会被重新排序memory_order_seq_cst 顺序一致性 memory_order_seq_cst 就像是memory_order_acq_rel的加强版它不管原子操作是属于读取还是写入的操作只要某个线程有用到memory_order_seq_cst 的原子操作线程中该memory_order_seq_cst 操作前的数据操作绝对不会被重新排在该memory_order_seq_cst 操作之后且该memory_order_seq_cst 操作后的数据操作也绝对不会被重新排在memory_order_seq_cst 操作前。
c20新增成员
在C20版本中添加了新的功能函数可以通过原子类型来阻塞线程和条件变量中的等待/通知函数是一样的。 2.原子变量的使用
假设我们要制作一个多线程交替数数的计数器我们使用互斥锁和原子变量的方式分别进行实现对比一下二者的差异
2.1互斥锁版本
#include iostream
#include thread
#include mutex
#include atomic
#include functional
using namespace std;struct Counter
{void increment(){for (int i 0; i 10; i){lock_guardmutex locker(m_mutex);m_value;cout increment number: m_value , theadID: this_thread::get_id() endl;this_thread::sleep_for(chrono::milliseconds(100));}}void decrement(){for (int i 0; i 10; i){lock_guardmutex locker(m_mutex);m_value--;cout decrement number: m_value , theadID: this_thread::get_id() endl;this_thread::sleep_for(chrono::milliseconds(100));}}int m_value 0;mutex m_mutex;
};int main()
{Counter c;auto increment bind(Counter::increment, c);auto decrement bind(Counter::decrement, c);thread t1(increment);thread t2(decrement);t1.join();t2.join();return 0;
}2.2原子变量版本
#include iostream
#include thread
#include atomic
#include functional
using namespace std;struct Counter
{void increment(){for (int i 0; i 10; i){m_value;cout increment number: m_value , theadID: this_thread::get_id() endl;this_thread::sleep_for(chrono::milliseconds(500));}}void decrement(){for (int i 0; i 10; i){m_value--;cout decrement number: m_value , theadID: this_thread::get_id() endl;this_thread::sleep_for(chrono::milliseconds(500));}}// atomicint atomic_intatomic_int m_value 0;
};int main()
{Counter c;auto increment bind(Counter::increment, c);auto decrement bind(Counter::decrement, c);thread t1(increment);thread t2(decrement);t1.join();t2.join();return 0;
}程序运行结果:
decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372
decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 1, theadID: 6372
decrement number: 0, theadID: 14916
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: 0, theadID: 14916
increment number: 0, theadID: 6372
decrement number: -1, theadID: 14916
increment number: 0, theadID: 6372总结:
通过代码的对比可以看出使用了原子变量之后就不需要再定义互斥量了在使用上更加简便并且这两种方式都能保证在多线程操作过程中数据的正确性不会出现数据的混乱。
原子类型atomicT 可以封装原始数据最终得到一个原子变量对象操作原子对象能够得到和操作原始数据一样的效果当然也可以通过store()和load()来读写原子对象内部的原始数据。