C关键字整理
atomic #include
原子操作(atomic): 互斥量的加锁一般是针对一个代码段,而原子操作针对的一般都是一个变量。原子变量既不可复制亦不可移动。 (1)它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。 是线程安全的。
(2)原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。
(3)C++11中所有的原子类都是 不允许拷贝、不允许Move 的,atomic_flag也不例外。
(4)C++11 对常见的原子操作进行了抽象,定义出统一的接口,并根据编译选项/环境产生平台相关的实现。新标准将原子操作定义为atomic模板类的成员函数,囊括了绝大多数典型的操作—— 读、写、比较、交换 等
contained type
atomic type
bool
atomic_bool
char
atomic_char
signed char
atomic_schar
unsigned char
atomic_uchar
short
atomic_short
unsigned short
atomic_ushort
int
atomic_int
unsigned int
atomic_uint
long
atomic_long
unsigned long
atomic_ulong
long long
atomic_llong
unsigned long long
atomic_ullong
wchar_t
atomic_wchar_t
char16_t
atomic_char16_t
char32_t
atomic_char32_t
intmax_t
atomic_intmax_t
uintmax_t
atomic_uintmax_t
int_least N _t
atomic_int_least N _t
uint_least N _t
atomic_uint_least N _t
int_fast N _t
atomic_int_fast N _t
uint_fast N _t
atomic_uint_fast N _t
intptr_t
atomic_intptr_t
uintptr_t
atomic_uintptr_t
size_t
atomic_size_t
ptrdiff_t
atomic_ptrdiff_t
(4)macro
macro
relative to types
ATOMIC_BOOL_LOCK_FREE
bool
ATOMIC_CHAR_LOCK_FREE char
signed char
unsigned char
ATOMIC_SHORT_LOCK_FREE short
unsigned short
ATOMIC_INT_LOCK_FREE int
unsigned int
ATOMIC_LONG_LOCK_FREE long
unsigned long
ATOMIC_LLONG_LOCK_FREE long long
unsigned long long
ATOMIC_WCHAR_T_LOCK_FREE
wchar_t
ATOMIC_CHAR16_T_LOCK_FREE
char16_t
ATOMIC_CHAR32_T_LOCK_FREE
char32_t
ATOMIC_POINTER_LOCK_FREE U*
(for any type U)
(5)memory_order:内存顺序
序号
值
意义
1
memory_order_relaxed
宽松模型,不对执行顺序做保证
2
memory_order_consume 当前线程中,满足happens-before原则。
当前线程中该原子的所有后续操作,必须在本条操作完成之后执行
3
memory_order_acquire 当前线程中, 读 操作满足happens-before原则。
所有后续的 读 操作必须在本操作完成后执行
4
memory_order_release 当前线程中, 写 操作满足happens-before原则。
所有后续的 写 操作必须在本操作完成后执行
5
memory_order_acq_rel
当前线程中,同时满足memory_order_acquire和memory_order_release
6
memory_order_seq_cst
最强约束。全部读写都按顺序执行
(6)Functions
(7)std::atomic的限制:trivially copyable( 可平凡复制): 一个类型如果是trivially copyable,则使用memcpy这种方式把它的数据从一个地方拷贝出来会得到相同的结果。
1.没有non-trivial 的拷贝构造函数
2.没有non-trivial的move构造函数
3.没有non-trivial的赋值操作符
4.没有non-trivial的move赋值操作符
5.有一个trivial的析构函数std::atomic_flag: 最简单的原子变量实例, 是对于bool类型的变量进行原子操作,提供了标志的管理,标志有三种状态:clear、set和未初始化状态。
接口介绍:
(1)ATOMIC_FLAG_INIT: 用于给atomic_flag变量赋初值,如果定义后为赋值,则状态是不确定的。被这个赋值后的状态为false。
(2)test_and_set() : 接口函数,调用后状态变为true,并返回改变状态前的状态值 。
(3)clear() : 接口函数,调用后状态变为false。#include
#include
#include
#include
std::atomic_flag lock = ATOMIC_FLAG_INIT ;
int gcnt = 0;
void f(int n)
{
for (int cnt = 0; cnt < 100; ++cnt) {
while (lock.test_and_set(std::memory_order_acquire) ) // 获得锁
; // 自旋
std::cout << "Output from thread " << n << " ";
gcnt++;
lock.clear(std::memory_order_release ); // 释放锁
}
}
int main()
{
std::vector v;
for (int n = 0; n < 10; ++n) {
v.emplace_back(f, n);
}
for (auto& t : v) {
t.join();
}
}
自旋锁的解释 :当某一个线程调用‘lock.test_and_set’时,即设置lock的状态为true,当另一个线程进入时,再次调用test_and_set时返回的状态为true,则一直在while循环中不断获取,即实现了等待,直到第一个线程调用clear将状态变为false。std::atomic: 通过atomic模板类可以对更多的类型进行原子操作
(1)is_lock_free: 通过这个接口判断是否需要加锁。如果是,则在多个线程访问该对象时不会导致线程阻塞(可能使用某种事务内存transactional memory方法实现lock-free的特性)。事实上该函数可以做为一个静态函数。所有指定相同类型T的atomic实例的is_lock_free函数都会返回相同值。
(2)store: 赋值操作。operator=实际上内部调用了store,并返回d。
void store(T desr, memory_order m = memory_order_seq_cst) noexcept;
void store(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
T operator =(T d) noexcept;
T operator =(T d) volatile noexcept;
(3)load: 读取,加载并返回变量的值。operator T是load的简化版,内部调用的是load(memory_order_seq_cst)形式。
(4)exchange: 交换,赋值后返回变量赋值前的值。exchange也称为read-modify-write操作。
T exchange(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
T exchange(T desr, memory_order m = memory_order_seq_cst) noexcept;
(5)compare_exchange_weak: 这就是有名的CAS(Compare And Swap: 比较并交换)。操作是原子的,排它的。其它线程如果想要读取或修改该原子对象时,会等待先该操作完成。
(6)compare_exchange_strong:
进行compare时,与weak版一样,都是比较的物理内容。与weak版不同的是,strong版本不会返回伪false。即:原子对象所封装的值如果与expect在物理内容上相同,strong版本一定会返回true。其所付出的代价是:在某些需要循环检测的算法,或某些平台下,其性能较compare_exchange_weak要差。但对于某些不需要采用循环检测的算法而言, 通常采用compare_exchange_strong 更好。
std::atomic特化:
(1)fetch_add: 该函数将原子对象封装的值加上v,同时返回原子对象的旧值。其功能用伪代码表示为:
// T is integral
T fetch_add(T v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_add(T v, memory_order m = memory_order_seq_cst) noexcept;
// T is pointer
T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept;
(2)fetch_sub: 该函数将原子对象封装的值减去v,同时返回原子对象的旧值。其功能用伪代码表示为:
// T is integral
T fetch_sub(T v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_sub(T v, memory_order m = memory_order_seq_cst) noexcept;
// T is pointer
T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept;
(3)++, --, +=, -=: 不管是基于整数的特化,还是指针特化,atomic均支持这四种操作。其用法与未封装时一样
独属于数值型特化的原子操作 - 位操作:
(1)fetch_and,fetch_or,fetch_xor:
位操作,将contained按指定方式进行位操作,并返回contained的旧值。
integral fetch_and(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_and(integral v, memory_order m = memory_order_seq_cst) noexcept;
integral fetch_or(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_or(integral v, memory_order m = memory_order_seq_cst) noexcept;
integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) noexcept;
(2)perator &=,operator |=,operator ^=: 与相应的fetch_*操作不同的是,operator操作返回的是新值
T operator &=(T v) volatile noexcept {return fetch_and(v) & v;}
T operator &=(T v) noexcept {return fetch_and(v) & v;}
T operator |=(T v) volatile noexcept {return fetch_or(v) | v;}
T operator |=(T v) noexcept {return fetch_or(v) | v;}
T operator ^=(T v) volatile noexcept {return fetch_xor(v) ^ v;}
T operator ^=(T v) noexcept {return fetch_xor(v) ^ v;}