C++程序中的所有数据都是由对象构成,对象仅仅时对C++数据的一个声明。在声明C++对象的时候需要对这个对象指定分配相应的内存空间。如果使用new在堆上分配内存空间需要调用相应的delete来将这块内存空间归还。就类似于在C语言中使用malloc/free来分配和释放内存空间一样。
无论对象时怎么样的一个类型,一个对象都会存储在一个或多个内存位置上。每一个内存为位置都是一个标量类型的对象。有四个需要牢记的原则:
- 每一个变量都是一个对象,包括作为其成员变量的对象
- 每个对象至少有一个内存位置
- 基本类型都有确定的内存位置
- 相邻位域时相同内存中一部分。
对于多线程应用而言,所有的数据都在内存中。当两个线程访问不同的内存位置时不会造成任何的问题,但是当程序访问同一个内存,如果只是读取那么也不会出现问题,但是要是写入的话,则有可能会发生data race。为了避免条件竞争,两个线程之间就需要一定的执行顺序。可以使用互斥量确定访问顺序;还有就是使用原子操作同步机制,决定两个线程的访问顺序。当多个线程访问同一个内存地址时,对每个访问都需要定义一个顺序。
每一个在C++程序中的对象,都有确定好的修改顺序。如果对象不是一个原子类型,必须确保同步操作使得每个线程都遵守不同变量的修改顺序,这样才不会发生data race或这未定义的行为,如果使用原子操作,那么只需交给编译器去负责就好。那么什么是原子操作呢?
原子操作是一类不可分割的操作,它的状态只能为完成或者未完成,不存在完成一半。标准原子类型可以在头文件
剩下的原子类型都可以通过特化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 |
通常,标准原子类型不能copy和赋值,他们没有copy constructor和copy assignment函数,但是为了可以隐式转化成对应的内置类型,这些类型依旧支持赋值。可以使用load()和store()成员函数,exchange(),compare_exchange_weak()和compare_exchange_strong()。都支持符合赋值符:+=,-=,*=,/=等等。并且整型和指针还支持++和–。也有对应的成员函数。
特化成员函数
fetch_add | 原子地将参数加到存储于原子对象的值,并返回先前保有的值 (公开成员函数) |
---|---|
fetch_sub | 原子地从存储于原子对象的值减去参数,并获得先前保有的值 (公开成员函数) |
fetch_and | 原子地进行参数和原子对象的值的逐位与,并获得先前保有的值 (公开成员函数) |
fetch_or | 原子地进行参数和原子对象的值的逐位或,并获得先前保有的值 (公开成员函数) |
fetch_xor | 原子地进行参数和原子对象的值的逐位异或,并获得先前保有的值 (公开成员函数) |
成员函数
(构造函数) | 构造原子对象 (公开成员函数) |
---|---|
operator= | 存储值于原子对象 (公开成员函数) |
is_lock_free | 检查原子对象是否免锁 (公开成员函数) |
store | 原子地以非原子对象替换原子对象的值 (公开成员函数) |
load | 原子地获得原子对象的值 (公开成员函数) |
operator T | 从原子对象加载值 (公开成员函数) |
exchange | 原子地替换原子对象的值并获得它先前持有的值 (公开成员函数) |
compare_exchange_weak,ompare_exchange_strong | 原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载 (公开成员函数) |
wait(C++20) | 阻塞线程直至被提醒且原子值更改 (公开成员函数) |
notify_one(C++20) | 提醒至少一个在原子对象上的等待中阻塞的线程 (公开成员函数) |
notify_all(C++20) | 提醒所有在原子对象上的等待中阻塞的线程 (公开成员函数) |
对于用户自定义类型,std::atmoic<>会有所限制:load()和store()(赋值和转换用户自定义类型),exchange(),compare_exchange_weak()和compare_exchange_strong()。每种类型都有一个可选内存排序参数,主要操作为以下三类:
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_seq_cst,memory_order_release,memory_order_acq_rel。
所有的默认顺序为memory_order_seq_cst