c++ 原子操作 赋值_5.2 C++中的原子操作和原子类型

5.2 C++中的原子操作和原子类型

原子操作 是个不可分割的操作。

在系统的所有线程中,你是不可能观察到原子操作完成了一半这种情况的;

它要么就是做了,要么就是没做,只有这两种可能。

如果从对象读取值的加载操作是 原子 的,而且对这个对象的所有修改操作也是 原子 的,

那么加载操作得到的值要么是对象的初始值,要么是某次修改操作存入的值。

另一方面,非原子操作可能会被另一个线程观察到只完成一半。

如果这个操作是一个存储操作,那么其他线程看到的值,可能既不是存储前的值,也不是存储的值,而是别的什么值。

如果这个非原子操作是一个加载操作,它可能先取到对象的一部分,然后值被另一个线程修改,然后它再取到剩余的部分,

所以它取到的既不是第一个值,也不是第二个值,而是两个值的某种组合。

正如第三章所讲的,这一下成了一个容易出问题的竞争冒险,

但在这个层面上它可能就构成了 数据竞争 (见5.1节),就成了未定义行为。

在C++中,多数时候你需要一个原子类型来得到原子的操作,我们来看一下这些类型。

5.2.1 标准原子类型

标准 原子类型 定义在头文件中。

这些类型上的所有操作都是原子的,在语言定义中只有这些类型的操作是原子的,不过你可以用互斥锁来 模拟 原子操作。

实际上,标准原子类型自己的实现就可能是这样模拟出来的:

它们(几乎)都有一个is_lock_free()成员函数,

这个函数让用户可以查询某原子类型的操作是直接用的原子指令(x.is_lock_free()返回true),

还是编译器和库内部用了一个锁(x.is_lock_free()返回false)。

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

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

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

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

原子类型

相关特化类

atomic_bool

std::atomic

atomic_char

std::atomic

atomic_schar

std::atomic

atomic_uchar

std::atomic

atomic_int

std::atomic

atomic_uint

std::atomic

atomic_short

std::atomic

atomic_ushort

std::atomic

atomic_long

std::atomic

atomic_ulong

std::atomic

atomic_llong

std::atomic

atomic_ullong

std::atomic

atomic_char16_t

std::atomic

atomic_char32_t

std::atomic

atomic_wchar_t

std::atomic

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

atomic_uint_fast64_t

uint_fast64_t

atomic_intptr_t

intptr_t

atomic_uintptr_t

uintptr_t

atomic_size_t

size_t

atomic_ptrdiff_t

ptrdiff_t

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
原子类型原子操作是并发编程的重要概念。在多线程编程,当多个线程同时操作同一个共享变量时,可能会出现数据竞争的问题,导致程序的结果不可预测。为了解决这个问题,C11引入了原子类型原子操作原子类型是一种特殊的数据类型,它的操作都是原子的,即不会被其他线程的操作干扰。C11提供了几种原子类型,包括原子整型、原子指针和原子布尔型等。原子类型可以在多线程环境下安全地进行读写操作,保证数据的一致性。 原子操作是对原子类型进行的操作,包括赋值、递增、递减等。这些操作都是原子的,不会被其他线程的操作干扰。原子操作通过一些特殊的语法和函数来实现,如原子操作的语法是“Atomics”开头的函数。例如,使用原子操作可以通过atomic_store函数将一个值存储到原子类型变量,保证线程安全。 使用原子类型原子操作可以简化并发编程的复杂度,避免数据竞争。原子类型原子操作提供了一种高效的并发编程模型,在多线程编程具有重要的应用价值。 在使用原子类型原子操作时,需要注意一些问题。首先,原子操作虽然保证了操作的原子性,但并不能完全解决所有的并发问题。其次,原子操作的性能可能不如普通操作,因为原子操作需要保证线程安全,可能需要加锁等额外开销。 总之,原子类型原子操作是C11提供的一种并发编程的解决方案。通过使用原子类型原子操作,可以有效解决多线程编程的数据竞争问题,提高程序的并发性和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值