C++并发编程实战——05.内存模型与原子操作

内存模型与原子操作

内存模型

无论是怎么样的类型,都会存储在一个或多个内存位置上。虽然相邻位域中是不同的对象,但仍视其为相同的内存位置。

struct s1{
	int i: 8;
	int j: 4;
	int a: 3;//8+4+3=15位<sizeof(int)即8字节(相同位域不同对象)
	double b;//8字节
};//8+8=16字节
struct s2{
	int i: 8;
	int j: 4;//8+4=12位<sizeof(int)即8字节(相同位域不同对象)
	double b;//8字节
	int a:3;//3位<sizeof(int)即8字节
};//8+8+8=24字节

四个需要牢记的原则:

  1. 每个变量都是对象,包括其成员变量的对象。
  2. 每个对象至少占有一个内存位置。
  3. 基本类型都有确定的内存位置(无论类型大小如何,即使他们是相邻的或是数组的一部分)。
  4. 相邻位域是相同内存中的一部分。

为了避免条件竞争,线程就要以一定的顺序执行。

  • 第一种方式:互斥量确定了线程访问的顺序,避免未定义行为的发生。
  • 另一种方式:原子操作未指定线程访问顺序,但拉回定义行为的区间。

在初始化开始阶段,线程对象确定好修改的顺序。大多数情况下,这个顺序不同于执行中的顺序,但在给定的程序中,所有线程都需要遵守这个顺序。

  • 非原子类型需要使用同步操作,使线程遵守修改顺序。
  • 而原子操作,编译器有责任去做同步。

所有线程都要遵守程序中每个独立对象修改顺序,但没有必要遵守在独立对象上的操作顺序

原子操作和原子类型

原子操作:不可再分割的操作(当然自然科学中有夸克,但是只是个名称不抬杠)。如果读取操作对象是原子操作,其它操作也是原子的。

标准原子类型

定义在头文件<atomic>中。语言中将文件中的类型定义为原子的,也能用互斥锁模拟原子操作。

大部分原子类型可通过特化std::atomic<>得到,几乎都有成员函数is_lock_free()

  • 如果原子操作是直接用原子CPU指令实现无锁,返回true;
  • 是内部使用锁结构,返回false。(内部没有互斥量的实现,才能有性能提升)

注:除std::atomic_flag不提供is_lock_free(),为简单布尔标志必然无锁。可以此为基础实现简单锁,进而实现其他基础原子类型。

C++17中有static constexpr bool is_always_lock_free;,返回true硬件上是无锁类型。

标准库提供了一组宏(对应于内置原子类型),编译时对各种整型原子操作是否无锁进行判别(有锁,其值为0;无锁,其值为2;无所状态运行时才能决定,其值为1):

  • ATOMIC_BOOL_LOCK_FREE

  • ATOMIC_CHAR_LOCK_FREE

  • ATOMIC_CHAR16_T_LOCK_FREE

  • ATOMIC_CHAR32_T_LOCK_FREE

  • ATOMIC_WCHAR_T_LOCK_FREE

  • ATOMIC_SHORT_LOCK_FREE

  • ATOMIC_INT_LOCK_FREE

  • ATOMIC_LONG_LOCK_FREE

  • ATOMIC_LLONG_LOCK_FREE

  • ATOMIC_POINTER_LOCK_FREE

内置原子类型备选名于其相关的std::stomic<>特化(避免两种混用):

原子类型 相关特化类
atomic_bool std::atomic
atomic_char std::atomic
atomic_schar std::atomic
atomic_uchar std::atomic
atomic_int std::atomic
atomic_short std::atomic
atomic_long std::atomic
atomic_llong std::atomic
atomic_char16_t std::atomic<char16_t>
atomic_char32_t std::atomic<char32_t>
atomic_wchar_t std::atomic<wchar_t>

标准原子类型定义和对应的内置类型定义:

原子类型定义 标准库中相关类型定义
atomic_int_least8_t int_least8_t
atomic_int_least16_t int_least16_t
atomic_int_least32_t int_least32_t
atomic_int_least64_t int_least64_t
atomic_int_fast8_t int_fast8_t
atomic_int_fast16_t int_fast16_t
atomic_int_fast32_t int_fast32_t
atomic_int_fast64_t int_fast64_t
atomic_intptr_t intptr_t
atomic_size_t size_t
atomic_ptrdiff_t ptrdiff_t
atomic_intmax_t intmax_t

注:原类型名前加上atomic_signed写为sunsigned写为ulong long写为ll

原子类型没有传统意义上的拷贝构造函数和拷贝赋值操作符(返回atomic&),但可隐式转化成对应内置类型(支持赋值)。赋值操作返回atomic而非atomic&;命名函数返回操作值。

//构造函数
atomic() noexcept = default;
constexpr atomic( T desired ) noexcept;
atomic( const atomic& ) = delete;//删除
//赋值
T operator=( T desired ) noexcept;
T operator=( T desired ) volatile noexcept;
atomic& operator=( const atomic& ) = delete;//删除
atomic& operator=( const atomic& ) volatile = delete;//删除
//std::atomic通过coyping函数实例化必须使用T类型而非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

每个原子类型及其所能使用的操作:

成员函数 atomic_flag atomic<bool> atomic<T*> atomic<内置类型> atomic<其他类型>
test_and_set Y
clear Y
is_lock_free Y Y Y Y
load Y Y Y Y
store Y Y Y Y
exchange Y Y Y Y
compare_exchange_weak, compare_exchange_strong Y Y Y Y
fetch_add, += Y Y
fetch_sub, -= Y Y
fetch_or, |= Y
fetch_and, &= Y
fetch_xor, ^= Y
++, – Y Y

没有除法、乘法及移位操作,但可以使用compare_exchange_weak()完成。

函数原子化操作࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值