第1节和第2节简单介绍g++ (GCC) 4.8.5
的atomic实现。
GCC的c++11 atomic 实现简介 (1) atomic_flag
(1.1)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 struct __atomic_flag_base { __atomic_flag_data_type _M_i; }; #define ATOMIC_FLAG_INIT { 0 } struct atomic_flag : public __atomic_flag_base{ atomic_flag() noexcept = default ; ~atomic_flag() noexcept = default ; atomic_flag(const atomic_flag&) = delete ; atomic_flag& operator =(const atomic_flag&) = delete ; atomic_flag& operator =(const atomic_flag&) volatile = delete ; constexpr atomic_flag (bool __i) noexcept : __atomic_flag_base { _S_init(__i) } { } bool test_and_set(memory_order __m = memory_order_seq_cst) noexcept { return __atomic_test_and_set (&_M_i, __m); } bool test_and_set(memory_order __m = memory_order_seq_cst) volatile noexcept { return __atomic_test_and_set (&_M_i, __m); } void clear(memory_order __m = memory_order_seq_cst) noexcept { memory_order __b = __m & __memory_order_mask; __glibcxx_assert(__b != memory_order_consume); __glibcxx_assert(__b != memory_order_acquire); __glibcxx_assert(__b != memory_order_acq_rel); __atomic_clear (&_M_i, __m); } void clear(memory_order __m = memory_order_seq_cst) volatile noexcept { memory_order __b = __m & __memory_order_mask; __glibcxx_assert(__b != memory_order_consume); __glibcxx_assert(__b != memory_order_acquire); __glibcxx_assert(__b != memory_order_acq_rel); __atomic_clear (&_M_i, __m); } private : static constexpr __atomic_flag_data_type _S_init(bool __i) { return __i ? __GCC_ATOMIC_TEST_AND_SET_TRUEVAL : 0 ; } };
atomic_flag
和下文讲的其他atomic类型不同,它只支持test_and_set
和clear
操作;默认构造函数构造的变量处于uninitialized状态(既没有被set也没有被clear);atomic_flag f = ATOMIC_FLAG_INIT
构造的变量处于clear状态;
atomic_flag
的内部是一个__atomic_flag_data_type _M_i
,__atomic_flag_data_type
是一个bool
或者unsigned char
:
1 2 3 4 5 #if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1 typedef bool __atomic_flag_data_type; #else typedef unsigned char __atomic_flag_data_type; #endif
test_and_set
和clear
操作是通过GCC的__atomic_test_and_set
和__atomic_clear
(见第2节),并基于这个_M_i
实现的。
__atomic_base
(1.2)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 template <typename _ITp> struct __atomic_base { private : typedef _ITp __int_type; __int_type _M_i; public : __atomic_base() noexcept = default ; ~__atomic_base() noexcept = default ; __atomic_base(const __atomic_base&) = delete ; __atomic_base& operator =(const __atomic_base&) = delete ; __atomic_base& operator =(const __atomic_base&) volatile = delete ; constexpr __atomic_base(__int_type __i) noexcept : _M_i (__i) { } ... };
这个模板是为基础类型提供的,基础类型包括以下(不包括float
, double
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 char signed char unsigned char short unsigned short int unsigned int long unsigned long long long unsigned long long char16_t char32_t wchar_t
所以,atomic_T
都是__atomic_base<T>
的别名(T是基础类型,bool类型除外):
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef __atomic_base<char > atomic_char ;typedef __atomic_base<signed char > atomic_schar ;typedef __atomic_base<unsigned char > atomic_uchar ;typedef __atomic_base<short > atomic_short ;typedef __atomic_base<unsigned short > atomic_ushort ;typedef __atomic_base<int > atomic_int ;typedef __atomic_base<unsigned int > atomic_uint ;typedef __atomic_base<long > atomic_long ;typedef __atomic_base<unsigned long > atomic_ulong ;typedef __atomic_base<long long > atomic_llong ;typedef __atomic_base<unsigned long long > atomic_ullong ;typedef __atomic_base<wchar_t > atomic_wchar_t ;...
这些基础类型都属于某种整数,支持:
加减运算++
, --
, +=
, -=
, fetch_add
, fetch_sub
;
位运算&=
, |=
、^=
, fetch_and
, fetch_or
, fetch_xor
操作。
atomic通用(atomic_flag
除外)操作:load
, store
, exchange
, compare_exchange_weak
, compare_exchange_strong
和is_lock_free
;
这些函数的实现都是基于GCC的__atomic
函数实现的(见第2节)。另外,bool
不支持加减运算和位运算,所以atomic_bool
不是__atomic_base<bool>
的别名(见第1.3节):
atomic_bool
(1.3)bool不支持数学运算和位运算,所以,atomic_bool
不是__atomic_base<bool>
的别名,而是通过单独的class atomic_bool
实现的,只不过atomic_bool
组合(而不是继承)一个__atomic_base<bool> _M_base
,并借助这个_M_base
实现bool支持的操作(atomic_bool
的函数f()
调用_M_base
的f()
),摒弃了不支持的操作;这也是通过组合而不是继承来实现的原因:继承的话,atomic_bool
就继承数学运算和位运算了。所以,atomic_bool
只支持atomic通用(atomic_flag
除外)操作:load
, store
, exchange
, compare_exchange_weak
, compare_exchange_strong
和is_lock_free
;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct atomic_bool { private : __atomic_base<bool > _M_base; public : atomic_bool () noexcept = default ; ~atomic_bool () noexcept = default ; atomic_bool (const atomic_bool &) = delete ; atomic_bool & operator =(const atomic_bool &) = delete ; atomic_bool & operator =(const atomic_bool &) volatile = delete ; constexpr atomic_bool (bool __i) noexcept : _M_base (__i) { } ... };
__atomic_base<_PTp*>
(1.4)针对指针类型,对于__atomic_base
(见第1.2节)的偏特化。例如operator++()
,对于上述基础类型来说,是数学上的加1;而对于指针类型,是加_M_type_size(1)
,即1*sizeof(_PTp)
;并且,特化时,摒弃了位运算。
atomic
(1.5)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename _Tp> struct atomic { private : _Tp _M_i; public : atomic() noexcept = default ; ~atomic() noexcept = default ; atomic(const atomic&) = delete ; atomic& operator =(const atomic&) = delete ; atomic& operator =(const atomic&) volatile = delete ; constexpr atomic (_Tp __i) noexcept : _M_i (__i) { } ... };
这个类不是基于__atomic_base
实现的,是一个全新的实现,主要用于:
浮点类型,自定义类型等;例如class Foo {...}; atomic<Foo>;
;
特化以支持基础类型(见1.6-1.8节);
只支持atomic通用(atomic_flag
除外)操作:load
, store
, exchange
, compare_exchange_weak
, compare_exchange_strong
和is_lock_free
;
atomic<_Tp*>
(1.6)针对指针类型,对于atomic
(见第1.5节)的偏特化;atomic<_Tp*>
组合一个__atomic_base<_Tp*> _M_b
。特化时,在atomic
上添加了++
, --
, +=
, -=
等操作,因为指针支持这些操作,实现上是调用_M_b
的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename _Tp> struct atomic <_Tp*> { typedef _Tp* __pointer_type; typedef __atomic_base<_Tp*> __base_type; __base_type _M_b; atomic() noexcept = default ; ~atomic() noexcept = default ; atomic(const atomic&) = delete ; atomic& operator =(const atomic&) = delete ; atomic& operator =(const atomic&) volatile = delete ; constexpr atomic (__pointer_type __p) noexcept : _M_b (__p) { } ... };
template<> struct atomic<bool> : public atomic_bool
(1.7)针对bool类型的特化;是继承atomic_bool
实现的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <> struct atomic <bool> : public atomic_bool { typedef bool __integral_type; typedef atomic_bool __base_type; atomic() noexcept = default ; ~atomic() noexcept = default ; atomic(const atomic&) = delete ; atomic& operator =(const atomic&) = delete ; atomic& operator =(const atomic&) volatile = delete ; constexpr atomic (__integral_type __i) noexcept : __base_type (__i) { } using __base_type::operator __integral_type; using __base_type::operator =; };
template<> struct atomic<char> : public atomic_char
(1.8)针对bool类型的特化;是继承atomic_char
实现的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template <> struct atomic <char> : public atomic_char { typedef char __integral_type; typedef atomic_char __base_type; atomic() noexcept = default ; ~atomic() noexcept = default ; atomic(const atomic&) = delete ; atomic& operator =(const atomic&) = delete ; atomic& operator =(const atomic&) volatile = delete ; constexpr atomic (__integral_type __i) noexcept : __base_type (__i) { } using __base_type::operator __integral_type; using __base_type::operator =; };
关系图与分类 (1.9) unsigned char, short, int, long, long long … 等都和char一样,不一一列出。最后以一张图表示它们的关系:
程序员应该使用绿色部分的类(模板)。atomic_flag
相当于一个自旋锁;atomic<bool>
, atomic<char>
, atomic<short>
, atomic<int>
等是基础类型的atomic版;atomic<_Tp*>
是指针类型的atomic版;atomic<Foo>
是自定义类型的atomic版。所以,可以把atomic类型分为以下几类:
atomic_flag
: 自旋锁
atomic<BaseType>
: char
, short
, int
, long
, long long
, unsigned char
, unsigned int
, …
atomic<bool>
: bool
atomic<T*>
: 指针
atomic<T>
: 浮点,自定义类型
支持的操作 (1.10) 基于第1.9节的分类,它们支持的操作如下:
Operation
atomic_flag
atomic<BaseType>
atomic<bool>
atomic<T*>
atomic<T>
test_and_set
,clear
Yes
++
,--
,+=
,-=
,fetch_add/sub
Yes
Yes
&=
, |=, ^=
,fetch_and/or/xor
Yes
is_lock_free
Yes
Yes
Yes
Yes
load
,store
Yes
Yes
Yes
Yes
exchange
,compare_exchange_weak/strong
Yes
Yes
Yes
Yes
GCC的__atomic
函数族 (2) __atomic
函数族包含的函数 (2.1)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 type __atomic_load_n (type *ptr, int memmodel); void __atomic_load (type *ptr, type *ret, int memmodel);void __atomic_store_n (type *ptr, type val, int memmodel);void __atomic_store (type *ptr, type *val, int memmodel);type __atomic_exchange_n (type *ptr, type val, int memmodel); void __atomic_exchange (type *ptr, type *val, type *ret, int memmodel);bool __atomic_compare_exchange_n (type *ptr, type *expected, type desired, bool weak, int success_memmodel, int failure_memmodel);bool __atomic_compare_exchange (type *ptr, type *expected, type *desired, bool weak, int success_memmodel, int failure_memmodel);type __atomic_add_fetch (type *ptr, type val, int memmodel); type __atomic_sub_fetch (type *ptr, type val, int memmodel); type __atomic_and_fetch (type *ptr, type val, int memmodel); type __atomic_xor_fetch (type *ptr, type val, int memmodel); type __atomic_or_fetch (type *ptr, type val, int memmodel); type __atomic_nand_fetch (type *ptr, type val, int memmodel); type __atomic_fetch_add (type *ptr, type val, int memmodel); type __atomic_fetch_sub (type *ptr, type val, int memmodel); type __atomic_fetch_and (type *ptr, type val, int memmodel); type __atomic_fetch_xor (type *ptr, type val, int memmodel); type __atomic_fetch_or (type *ptr, type val, int memmodel); type __atomic_fetch_nand (type *ptr, type val, int memmodel); bool __atomic_test_and_set (void *ptr, int memmodel);void __atomic_clear (bool *ptr, int memmodel);void __atomic_thread_fence (int memmodel);__atomic_signal_fence (int memmodel); bool __atomic_always_lock_free (size_t size, void *ptr);bool __atomic_is_lock_free (size_t size, void *ptr);
__atomic
函数族和c++11 atomic的关系 (2.2)第1节讲的是GCC对c++11标准中的atomic的实现,这些实现是基于GCC内的__atomic
函数族的。__atomic
函数族取代了之前旧的__sync
函数族,主要区别是__atomic
函数族允许用户指定memory-order/memory-model(作为参数),而__sync
函数族不支持。__atomic
函数族匹配了c++11内存模型的需求。实际上,__atomic
函数族和c++11 atomic如此匹配以至于让人感觉它就是为了实现c++11而生的,但理论上讲,GCC是GNU Compliler Collection,__atomic
函数族也可以用于c++11以外的其他编译器。
另外,我们可以直接使用__atomic
函数族:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> int main () { long long a, b; __atomic_store_n(&a, 100 , __ATOMIC_SEQ_CST); b = __atomic_load_n(&a, __ATOMIC_SEQ_CST); std ::cout << b << std ::endl ; return 0 ; }
这段程序可以使用g++ (GCC) 4.8.5
编译并运行(注意不用include额外的头文件),但它可移植性非常差:使用GCC以外的编译器无法编译,甚至其他版本的GCC也可能无法编译(假如若干版本以后,__atomic
函数族被替换了呢)。我们应该使用c++11的atomic,假如__atomic
函数族被替换了,那么GCC也会基于新的函数族重新实现c++11 atomic(如果那时还兼容c++11的话)。
系统相关和系统无关的实现 (2.3) __atomic
函数族的实现包含两部分:
1,2,4,8字节的基础类型和指针类型:所有__atomic
函数族都支持这些类型,系统结构无关部分 提供了这些支持(系统相关的库可以覆盖吗?)。也就是说,无需安装并链接系统结构相关的库,就可以使用__atomic
函数族来操作这些类型。 16字节的基础类型:如果系统结构支持,则__atomic
函数族就都支持。所以,这些支持是系统相关的库提供的。也就是说,必须安装并链接系统相关的库(对于x64是libatomic-4.8.5-36.el7_6.2.x86_64
)才能使用。 其他任意类型T
:只有__atomic_load
, __atomic_store
, __atomic_exchange
, __atomic_compare_exchange
4个函数支持,这4个函数是__atomic_load_n
, __atomic_store_n
, __atomic_exchange_n
, __atomic_compare_exchange_n
generic版。若T
的size
是1,2,4,8字节,则系统结构无关部分 就可以支持它们。否则,需要系统结构相关的库。
例如,下面这段代码不比链接libatomic-4.8.5-36.el7_6.2.x86_64
就可以编译并运行(也就是说,编译时就能确定系统结构无关部分 就能支持struct A
类的对象):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> struct A {int a; int b;};int main () { std ::cout << std ::boolalpha << __atomic_is_lock_free(sizeof (A), NULL ) << std ::endl ; A a1; A a2 = {.a=400 , .b=800 }; __atomic_store(&a1, &a2, __ATOMIC_RELAXED); std ::cout << a1.a << std ::endl ; std ::cout << a1.b << std ::endl ; return 0 ; }
而下面这段代码必须链接libatomic-4.8.5-36.el7_6.2.x86_64
(编译时就知道系统结构无关部分 不能支持struct A
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> struct A {int a; int b; int c; int d;};int main () { std ::cout << std ::boolalpha << __atomic_is_lock_free(sizeof (A), NULL ) << std ::endl ; A a1; A a2 = {.a=400 , .b=800 , .c=1600 , .d=3200 }; __atomic_store(&a1, &a2, __ATOMIC_RELAXED); std ::cout << a1.a << std ::endl ; std ::cout << a1.b << std ::endl ; std ::cout << a1.c << std ::endl ; std ::cout << a1.d << std ::endl ; return 0 ; }
甚至,下面这两段代码,前者可以不链接系统相关库,而后者必须链接:
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int main () { std ::cout << std ::boolalpha << __atomic_is_lock_free(8 , NULL ) << std ::endl ; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> int main () { std ::cout << std ::boolalpha << __atomic_is_lock_free(16 , NULL ) << std ::endl ; return 0 ; }
memory-order/memory-model (2.4) 不同的线程在不同cpu上执行,它们都有自己的cache。它们对共享变量的修改可能只在自己的cache里。memory-model合并了两个方面需求:
optimization barrier;
线程间sync;
__ATOMIC_RELAXED
(2.4.1)最松散模式,没有optimization barrier,也没有线程间sync;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //thread-1 y.store(20, memory_order_relaxed) x.store(10, memory_order_relaxed) //thread-2 if (x.load(memory_order_relaxed) == 10) { assert(y.load(memory_order_relaxed) == 20) /* assert A */ y.store (10, memory_order_relaxed) } //thread-3 if (y.load (memory_order_relaxed) == 10) assert (x.load(memory_order_relaxed) == 10) /* assert B */
thread-2的assert A
和thread-3的assert B
都可能失败。在__ATOMIC_RELAXED
模式下,一个线程不能依赖另一个线程内的语句顺序(可能被重排)。__ATOMIC_RELAXED
只保证这样一个顺序:一旦thread-A对变量的修改被thread-B看见,那么thread-B就再也看不见这个变量以前的值(不能一会儿看见新值一会儿看见旧值)。
假设x
的初始值是0:
1 2 3 4 5 6 7 8 thread-1 x.store (1, memory_order_relaxed) x.store (2, memory_order_relaxed) thread-2 y = x.load (memory_order_relaxed) z = x.load (memory_order_relaxed) assert (y <= z)
thread-2的assert
永远不会失败,即thread-2一旦看见x=1
就再看不见x=0
;一旦看见x=2
就再看不见x=1
或x=0
;
当程序员只想要一个变量是原子的,而不依赖这个变量来为其他共享数据提供线程间同步的时候,应该使用__ATOMIC_RELAXED
模式。这种模式要求最松散,所以编译器能提供更大的优化。
__ATOMIC_RELAXED
模式总结:
一个线程不能依赖另一个线程内的不相关语句的执行顺序;
可以依赖对同一个值得修改顺序;
只保证一个变量是原子的,不能依靠它为其他共享数据提供线程间同步;
__ATOMIC_CONSUME
(2.4.2)__ATOMIC_ACQUIRE
(2.4.3)__ATOMIC_RELEASE
(2.4.4)__ATOMIC_ACQ_REL
(2.4.5)__ATOMIC_SEQ_CST
(2.4.6)__ATOMIC_SEQ_CST
是sequentially consistent的缩写。它是最严格的模式,也是默认模式。
例1:
1 2 3 //thread-1 //thread-2 y = 1 if (x.load() == 2) x.store(2); assert (y == 1)
在__ATOMIC_SEQ_CST
模式下,thread-2里的assert
不可能失败。x
和y
是不相关的,本来编译器可以重排y=1
和x.store(2)
的顺序(这样thread-2的assert
就会失败),但__ATOMIC_SEQ_CST
要求线程间满足sequentially consistent:不相关的变量也必须满足happens-before关系,即,如果thread-2看到了x.store(2)
操作,那么它必须要看到x.store(2)
之前的所有操作(y=1
)。编译优化器不能随意的跨atomic.store()
来重排语句顺序。当然,也不能跨atomic.load()
来重排语句顺序,看例2。
例2:
1 2 3 4 5 6 7 8 9 a = 0 y = 0 b = 1 ------------------------------------------------------ //thread-1 //thread-2 x = a.load() while (y.load() != b) y.store (b) ; while (a.load() == x) a.store(1) ;
单看thread-1,while
内有两条语句:1. a.load()
; 2. 判断和x
是否相等并跳转(je
指令)。程序员的原意是这两个语句交替执行,直到a
和x
不相等。不使用__ATOMIC_SEQ_CST
的话,由于a
和x
是不相关的,编译器可能把它优化成一个死循环(跨atomic.load()
来重排语句顺序)。
例3:
假定x
和y
的初始值都是0.
1 2 3 4 5 6 7 8 9 10 11 //thread-1 y.store(20); //thread-2 x.store(10); //thread-3 assert (y.load() == 20 && x.load() == 0) //thread-4 assert (y.load() == 0 && x.load() == 10)
在例1里,y=1
和x.store(2)
这两个不相关的操作在同一个线程里 ,而例3要表达的是,在__ATOMIC_SEQ_CST
模式下,y.store(20)
和x.store(10)
(这里y
也是一个atomic变量)这两个不相关的操作,在不同的线程里 也有严格的顺序。虽然这个顺序是在运行时 决定的,但是一旦决定之后,所有线程都能看到相同的顺序:看见后者的时候,一定要求看见前者。
假如运行时 确定y.store(20)
在前x.store(10)
在后,那么thread-4的assert
一定会失败;
假如运行时 确定x.store(10)
在前y.store(20)
在后,那么thread-3的assert
一定会失败;
thread-3和thread-4的assert
不可能都通过。也就是说,atomic变量在线程间也是满足happens-before关系。
__ATOMIC_SEQ_CST
模式总结:
满足happens-before关系:不相关的atomic操作也必须满足这个关系。一旦一个atomic操作被看见,那么它之前的操作结果都必须被看见。不同的线程内的atomic操作也是一样,只不过它们谁先谁后是运行时 决定的。这个模式保证”consistency across all threads”,需要系统总线上的sync(可能比较expensive)。
atomic操作等同于optimization barrier:可以在atomic操作之间调整语句顺序,不能跨atomic操作调整语句顺序。其实这是happens-before原则衍生出来的:要所有atomic操作满足happens-before关系,自然不能跨atomic操作进行语句顺序调整。
lock free (2.5) __atomic_always_lock_free
在系统结构无关的实现 中(见第2.5节):GCC要能确定atomic变量不需要使用锁,就返回true;否则返回false。返回false不代表atomic变量必须使用锁,而是表示不知道:取决于系统结构。__atomic_is_lock_free
,假如能确定atomic变量不需要使用锁,就直接返回true(编译链接时就能确定,不需要链接系统结构相关的库)。否则,调用系统结构相关的库。
GCC不确定:
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int main () { std ::cout << std ::boolalpha << __atomic_always_lock_free(16 , NULL ) << std ::endl ; return 0 ; }
在x64系统结构下,16字节的atomic变量不需要锁:
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int main () { std ::cout << std ::boolalpha << __atomic_is_lock_free(16 , NULL ) << std ::endl ; return 0 ; }
c++11 atomic (3) 前面的内容都是在说GCC实现,这一节是c++11 atomic的标准,和实现无关。
memory order (3.1) 对应GCC的memory-order/memory-model(见第2.4节)。
1 2 3 4 5 6 memory_order_relaxed memory_order_consume memory_order_acquire memory_order_release memory_order_acq_rel memory_order_seq_cst
atomic_is_lock_free
(3.2)std::atomic_is_lock_free
函数用于查看一个std::atomic<T>* obj
是否是lock free的。
在头文件中,定义了这些宏
1 2 3 4 5 6 7 8 9 10 #define ATOMIC_BOOL_LOCK_FREE #define ATOMIC_CHAR_LOCK_FREE #define ATOMIC_CHAR16_T_LOCK_FREE #define ATOMIC_CHAR32_T_LOCK_FREE #define ATOMIC_WCHAR_T_LOCK_FREE #define ATOMIC_SHORT_LOCK_FREE #define ATOMIC_INT_LOCK_FREE #define ATOMIC_LONG_LOCK_FREE #define ATOMIC_LLONG_LOCK_FREE #define ATOMIC_POINTER_LOCK_FREE
它们的展开值:
若为0:nerver lock free;即当前编译器选择使用锁来实现atomic。
若为1:系统结构相关;
若为2:always lock free;即当前编译器(例如GCC 4.8.5)能够提供lock free的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> int main () { std ::cout << "ATOMIC_BOOL_LOCK_FREE : " << ATOMIC_BOOL_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_CHAR_LOCK_FREE : " << ATOMIC_CHAR_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_CHAR16_T_LOCK_FREE : " << ATOMIC_CHAR16_T_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_CHAR32_T_LOCK_FREE : " << ATOMIC_CHAR32_T_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_WCHAR_T_LOCK_FREE : " << ATOMIC_WCHAR_T_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_SHORT_LOCK_FREE : " << ATOMIC_SHORT_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_INT_LOCK_FREE : " << ATOMIC_INT_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_LONG_LOCK_FREE : " << ATOMIC_LONG_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_LLONG_LOCK_FREE : " << ATOMIC_LLONG_LOCK_FREE << std ::endl ; std ::cout << "ATOMIC_POINTER_LOCK_FREE : " << ATOMIC_POINTER_LOCK_FREE << std ::endl ; return 0 ; }
这表明,GCC 4.8.5
能够为这些类型提供无锁atomic实现(不管在何种系统结构上)。 另外需要提一下,atomic_flag
一定是lock free的,这是c++11标准规定的(只要支持c++11的编译器,就得提供atomic_flag
的lock free实现),所以atomic_flag
都不支持is_lock_free
函数(见第1.10节的表)
在x64系统结构下,这段程序运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> #include <atomic> struct A1 {char a;};struct A2 {short a;};struct A4 {char a[4 ];};struct A8 {int a; short b; char c[2 ];};struct A16 {int a[4 ];};struct A32 {int a[8 ];};struct A12 {int a, b, c;};struct PaddedA12 {int a, b, c; char pad[4 ];};int main () { std ::atomic<A1> a1; std ::atomic<A2> a2; std ::atomic<A4> a4; std ::atomic<A8> a8; std ::atomic<A16> a16; std ::atomic<A32> a32; std ::atomic<A12> a12; std ::atomic<PaddedA12> pa12; std ::cout << std ::boolalpha << "std::atomic<A1> lock free: " << std ::atomic_is_lock_free(&a1) << std ::endl << "std::atomic<A2> lock free: " << std ::atomic_is_lock_free(&a2) << std ::endl << "std::atomic<A4> lock free: " << std ::atomic_is_lock_free(&a4) << std ::endl << "std::atomic<A8> lock free: " << std ::atomic_is_lock_free(&a8) << std ::endl << "std::atomic<A16> lock free: " << std ::atomic_is_lock_free(&a16) << std ::endl << "std::atomic<A32> lock free: " << std ::atomic_is_lock_free(&a32) << std ::endl << "std::atomic<A12> lock free: " << std ::atomic_is_lock_free(&a12) << std ::endl << "std::atomic<PaddedA12> lock free: " << std ::atomic_is_lock_free(&pa12) << std ::endl ; return 0 ; }
这表明在x64系统结构下,对于1,2,4,8,16字节的数据,GCC提供lock free的atomic实现。当然,通过前面的分析我们知道,对于1,2,4,8字节的数据,GCC无需依赖系统结构就能提供lock free的实现(把std::atomic<A16>
, std::atomic<A32>
, std::atomic<A12>
, std::atomic<PaddedA12>
注释掉,可以不链接libatomic库);16字节数据能够lock free,是因为x64系统结构支持。