走进C++11(三十七)原子操作之 std::atomic

图片

 

关注公众号获取更多信息:

 

 

C++11提供了一个原子类型std::atomic<T>,可以使用任意类型作为模板参数,C++11内置了整型的原子变量,可以方便的使用原子变量,使用原子变量就不用互斥变量来保护该变量了。

 

每个 std::atomic 模板的实例化和全特化定义一个原子类型。若一个线程写入原子对象,同时另一线程从它读取,则行为良好定义(数据竞争的细节我们会在下节--内存模型里讲解)

 

另外,对原子对象的访问可以建立线程间同步,并按 std::memory_order 所对非原子内存访问定序。 

 

需要注意的是,std::atomic 既不可复制亦不可移动。 

 

由于std::atomic 是一个模板,C++11中定义了该模板特化的一些要求:

 

主模板
 

主 std::atomic 模板可用任何满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssignable) 的可平凡复制 (TriviallyCopyable)  类型 T 特化。若下列任何值为 false 则程序为谬构:

  • std::is_trivially_copyable<T>::value

  • std::is_copy_constructible<T>::value

  • std::is_move_constructible<T>::value

  • std::is_copy_assignable<T>::value

  • std::is_move_assignable<T>::value

std::atomic<bool> 使用初等模板。它保证是标准布局结构体。 

 

偏特化

 

标准库为下列类型提供 std::atomic 模板的特化,它们拥有初等模板所不拥有的额外属性:

 

对所有指针类型的部分特化 std::atomic<U*> 。这些特化拥有标准布局、平凡默认构造函数 (C++20 前)和平凡析构函数。除了为所有原子类型提供的操作,这些特化额外支持适合指针类型的原子算术运算,例如 fetch_add 、 fetch_sub 。 

 

对整数类型的特化

 

以下列整数类型之一实例化时, std::atomic 提供适合于整数类型的额外原子操作,例如 fetch_add 、 fetch_sub 、 fetch_and 、 fetch_or 、 fetch_xor :

  •  

    • 字符类型 char 、 char8_t  (C++20 起)、 char16_t 、 char32_t 和 wchar_t ;

    • 标准有符号整数类型:signed char 、 short 、 int 、 long 和 long long ;

    • 标准无符号整数类型:unsigned char 、 unsigned short 、 unsigned int 、 unsigned long 和 unsigned long long ;

    • 任何头文件 <cstdint> 中的 typedef 所需的额外整数类型。

另外,结果的 std::atomic<Integral> 特化拥有标准布局、平凡默认构造函数 (C++20 前)和平凡析构函数。定义有符号整数算术为使用补码;无未定义结果。 

 

atomic 用法

 

这次我们就不一一介绍atomic的API了,我们从atomic的定义反观atomic的用法:

 

template < class T > struct atomic {    bool is_lock_free() const volatile;    bool is_lock_free() const;    void store(T, memory_order = memory_order_seq_cst) volatile;    void store(T, memory_order = memory_order_seq_cst);    T load(memory_order = memory_order_seq_cst) const volatile;    T load(memory_order = memory_order_seq_cst) const;    operator  T() const volatile;    operator  T() const;    T exchange(T, memory_order = memory_order_seq_cst) volatile;    T exchange(T, memory_order = memory_order_seq_cst);    bool compare_exchange_weak(T &, T, memory_order, memory_order) volatile;    bool compare_exchange_weak(T &, T, memory_order, memory_order);    bool compare_exchange_strong(T &, T, memory_order, memory_order) volatile;    bool compare_exchange_strong(T &, T, memory_order, memory_order);    bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst) volatile;    bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst);    bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile;    bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst);    atomic() = default;    constexpr atomic(T);    atomic(const atomic &) = delete;    atomic & operator=(const atomic &) = delete;    atomic & operator=(const atomic &) volatile = delete;    T operator=(T) volatile;    T operator=(T);};

 

可以看出,我们常用的API如下:

 

(构造函数)

构造原子对象
(公开成员函数)

operator=

存储值于原子对象
(公开成员函数)

is_lock_free

检查原子对象是否免锁
(公开成员函数)

store

原子地以非原子对象替换原子对象的值
(公开成员函数)

load

原子地获得原子对象的值
(公开成员函数)

operator T

从原子对象加载值
(公开成员函数)

exchange

原子地替换原子对象的值并获得它先前持有的值
(公开成员函数)

compare_exchange_weakcompare_exchange_strong

原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载
(公开成员函数)

 

除了这些常用的API,我们常用的API还有一些特化的API(之前我们说过,只有整数类型和指针类型有特化,所以这些API只有整数类型和指针类型可用):

 

fetch_add

原子地将参数加到存储于原子对象的值,并返回先前保有的值
(公开成员函数)

fetch_sub

原子地从存储于原子对象的值减去参数,并获得先前保有的值
(公开成员函数)

fetch_and

原子地进行参数和原子对象的值的逐位与,并获得先前保有的值
(公开成员函数)

fetch_or

原子地进行参数和原子对象的值的逐位或,并获得先前保有的值  
(公开成员函数)

fetch_xor

原子地进行参数和原子对象的值的逐位异或,并获得先前保有的值  
(公开成员函数)

operator++operator++(int)operator--operator--(int)

令原子值增加或减少一
(公开成员函数)

operator+=operator-=operator&=operator|=operator^=

加、减,或与原子值进行逐位与、或、异或
(公开成员函数)

 

 

例子

 

下面我们用一个例子结束本文:

 

// constructing atomics#include <iostream>       // std::cout#include <atomic>         // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT#include <thread>         // std::thread, std::this_thread::yield#include <vector>         // std::vector
std::atomic<bool> ready (false);std::atomic_flag winner = ATOMIC_FLAG_INIT;
void count1m (int id) {  while (!ready) { std::this_thread::yield(); }      // wait for the ready signal  for (volatile int i=0; i<1000000; ++i) {}          // go!, count to 1 million  if (!winner.test_and_set()) { std::cout << "thread #" << id << " won!\n"; }};
int main (){  std::vector<std::thread> threads;  std::cout << "spawning 10 threads that count to 1 million...\n";  for (int i=1; i<=10; ++i) threads.push_back(std::thread(count1m,i));  ready = true;  for (auto& th : threads) th.join();
  return 0;}

 

可能的输出:

 

spawning 10 threads that count to 1 million...thread #7 won!
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值