C++11中的原子量和内存序详解

2020-01-06 19:27:01于海丽

 

这种重排有可能会导致一个线程内相互之间不存在依赖关系的指令交换执行顺序,以获得更高的执行效率。比如上面:flag 与 a 在A线程看起来是没有任何依赖关系,似乎执行顺序无关紧要。但问题在于B使用了flag作为是否读取a的依据,A的指令重排可能会导致step3的时候断言失败。

解决方案

一个比较稳妥的办法就是对于共享变量的访问进行加锁,加锁可以保证对临界区的互斥访问,例如第一种场景如果加锁后再执行i++ 然后解锁,则同一时刻只会有一个线程在执行i++ 操作。另外,加锁的内存语义能保证一个线程在释放锁前的写入操作一定能被之后加锁的线程所见(即有happens before 语义),可以避免第二种场景中读取到错误的值。

那么如果觉得加锁操作过重太麻烦而不想加锁呢?C++11提供了一些原子变量与原子操作来支持。

二、 C++11的原子量

C++11标准在标准库atomic头文件提供了模版atomic<>来定义原子量:


template< class T >
struct atomic;

它提供了一系列的成员函数用于实现对变量的原子操作,例如读操作load,写操作store,以及CAS操作compare_exchange_weak/compare_exchange_strong等。而对于大部分内建类型,C++11提供了一些特化:


std::atomic_bool std::atomic<bool>
std::atomic_char std::atomic<char>
std::atomic_schar std::atomic<signed char>
std::atomic_uchar std::atomic<unsigned char>
std::atomic_short std::atomic<short>
std::atomic_ushort std::atomic<unsigned short>
std::atomic_int std::atomic<int>
std::atomic_uint std::atomic<unsigned int>
std::atomic_long std::atomic<long>
······
//更多类型见:http://www.easck.com/w/cpp/atomic/atomic