5.2.1 标准原子类型

5.2.1 标准原子类型

标准原子类型(atomic types)可以在头文件中找到。所有在这种类型上的操作都是原子的,虽然可以使用互斥量去达到原子操作的效果,但只有在这些类型上的操作是原子的(语言明确定义)。实际上,标准原子类型都很相似:它们(大多数)都有一个is_lock_free()成员函数,这个函数允许用户决定是否直接对一个给定类型使用原子指令(x.is_lock_free()返回true),或对编译器和运行库使用内部锁(x.is_lock_free()返回false)。

只用std::atomic_flag类型不提供is_lock_free()成员函数。这个类型是一个简单的布尔标 志,并且在这种类型上的操作都需要是无锁的(lock-free);当你有一个简单无锁的布尔标志 是,你可以使用其实现一个简单的锁,并且实现其他基础的原子类型。当你觉得“真的很简 单”时,就说明:在 std::atomic_flag 对象明确初始化后,做查询和设置(使用test_and_set() 成员函数),或清除(使用clear()成员函数)都很容易。这就是:无赋值,无拷贝,没有测试和清 除,没有其他任何操作。

剩下的原子类型都可以通过特化std::atomic<>类型模板而访问到,并且拥有更多的功能,但可能不都是无锁的(如之前解释的那样)。在最流行的平台上,期望原子变量都是无锁的内置类型(例如 std::atomic<int>std::atomic<void*>),但这没有必要。你在后面将会看到,每个特化接口所反映出的类型特点;位操作(如&=)就没有为普通指针所定义,所以它也就不能为原 子指针所定义。

除了直接使用std::atomic<>类型模板外,你可以使用在表5.1中所示的原子类型集。由于历 史原因,原子类型已经添加入C++标准中,这些备选类型名可能参考相应的std::atomic<>特 化类型,或是特化的基类。在同一程序中混合使用备选名与std::atomic<>特化类名,会使代码的移植大打折扣

表5.1 标准原子类型的备选名和与其相关的  std::atomic<> 特化类

原子类型

相关特化类

atomic_bool

std::atomic<bool>

atomic_char

std::atomic<char>

atomic_schar

std::atomic<signed char>

atomic_uchar

std::atomic<unsigned char>

atomic_int

std::atomic<int>

atomic_uint

std::atomic<unsigned>

atomic_short

std::atomic<short>

atomic_ushort

std::atomic<unsigned short>

atomic_long

std::atomic<long>

atomic_ulong

std::atomic<unsigned long>

atomic_llong

std::atomic<long long>

atomic_ullong

std::atomic<unsigned long long>

atomic_char16_t

std::atomic<char16_t>

atomic_char32_t

std::atomic<char32_t>

atomic_wchar_t

std::atomic<wchar_t>

C++标准库不仅提供基本原子类型,还定义了与原子类型对应的非原子类型,就如同标准库中的  std::size_t 。如表5.2所示这些类型:

表5.2 标准原子类型定义(typedefs)和对应的内置类型定义(typedefs)

原子类型定义

标准库中相关类型定义

atomic_int_least8_t

int_least8_t

atomic_uint_least8_t

uint_least8_t

atomic_int_least16_t

int_least16_t

atomic_uint_least16_t

uint_least16_t

atomic_int_least32_t

int_least32_t

atomic_uint_least32_t

uint_least32_t

atomic_int_least64_t

int_least64_t

atomic_uint_least64_t

uint_least64_t

atomic_int_fast8_t

int_fast8_t

atomic_uint_fast8_t

uint_fast8_t

atomic_int_fast16_t

int_fast16_t

atomic_uint_fast16_t

uint_fast16_t

atomic_int_fast32_t

int_fast32_t

atomic_uint_fast32_t

uint_fast32_t

atomic_int_fast64_t

int_fast64_t

好多种类型!不过,它们有一个相当简单的模式;对于标准类型进行typedef T,相关的原子类型就在原来的类型名前加上atomic_的前缀:atomic_T。除了singed类型的缩写是s, unsigned的缩写是u,和long long的缩写是llong之外,这种方式也同样适用于内置类型。对 于  std::atomic<T> 模板,使用对应的T类型去特化模板的方式,要好于使用别名的方式。

通常,标准原子类型是不能拷贝和赋值,他们没有拷贝构造函数和拷贝赋值操作。但是,因为可以隐式转化成对应的内置类型,所以这些类型依旧支持赋值,可以使用load()store()成员函数,exchange()compare_exchange_weak()compare_exchange_strong()它们都支持复合赋值符:+=, -=, *=, |= 等等。并且使用整型和指针的特化类型还支持 ++ 和 --。当 然,这些操作也有功能相同的成员函数所对应:fetch_add(), fetch_or() 等等。返回值通过赋值操作返回,并且成员函数不是对值进行存储(在有赋值符操作的情况下),就是对值进行操作(在命名函数中)。这就能避免赋值操作符返回引用。为了获取存储在引用的的值,代码需要执行单独的读操作,从而允许另一个线程在赋值和读取进行的同时修改这个值,这也就为条件竞争打开了大门。

std::atomic<> 类模板不仅仅是一套特化的类型,其作为一个原发模板也可以使用用户定义类型创建对应的原子变量。因为,它是一个通用类模板,很多成员函数的操作在这种情况下有所限制:load(), store()(赋值和转换为用户类型), exchange(), compare_exchange_weak()和 compare_exchange_strong()。

每种函数类型的操作都有一个可选内存排序参数,这个参数可以用来指定所需存储的顺序。 在5.3节中,会对存储顺序选项进行详述。现在,只需要知道操作分为三类:

1)Store操作,可选如下顺序:memory_order_relaxed, memory_order_release, memory_order_seq_cst。

2)Load操作,可选如下顺序:memory_order_relaxed, memory_order_consume,memory_order_acquire, memory_order_seq_cst。

3)Read-modify-write(读-改-写)操作,可选如下顺序:memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst。 所有操作的默认顺序都是memory_order_seq_cst。

现在,让我们来看一下每个标准原子类型进行的操作,就从  std::atomic_flag 开始吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值