2022-09-04 C++并发编程(十八)
C++并发编程(十八)
- 前言
- 一、操作标准整数原子类
- 总结
前言
对于标准的整数(int, unsigned, long等),C++ 特化了标准整数原子类,相应会有一些算数函数,当然,有一些计算不包括在内,比如乘除。
一、操作标准整数原子类
与前述原子布尔类和原子指针类相同,标准的原子整数类也是用普通整数类初始化,
可判断是否为无锁结构,有读取和写入,原子交换,原子比较交换等操作,并且可以设置原子操作的内存次序。
//初始化整数原子类对象
std::atomic<int> atomicInt(0);
std::atomic<unsigned> atomicUInt(0);
std::atomic<long> atomicLong(0);
//判断是否是无锁结构
std::cout << atomicInt.is_lock_free() << std::endl;
//原子读取,可配合读操作内存次序
int testInt = atomicInt.load();
//原子写入,可配合写操作内存次序
atomicInt.store(1);
//原子交换,返回原值,更新值,可配合读改写操作内存次序
testInt = atomicInt.exchange(2);
//原子比较交换,与期望值(testInt)比较,相同则更新值,否则将值赋值给期望值(testInt)
// weak 版本可能出现虚假失败
//可配合两个内存次序,第一个是读改写(成功更新值),第二个是读(更新值失败)
while (!atomicInt.compare_exchange_weak(testInt, 3))
{
}
//原子比较交换,与weak版的区别是不会出现虚假失败
atomicInt.compare_exchange_strong(testInt, 4);
对于标准原子整数类对象,可以进行原子加,减,逻辑与,逻辑或,逻辑异或的函数操作,都可添加读改写类内存次序参数。
//原子加法,返回原值,更新完成加操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_add(1);
//原子减法,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_sub(1);
//原子与操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_and(1);
//原子或操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_or(1);
//原子异或操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_xor(1);
标准整数原子类有比较全面的运算符操作,如自增自减,复合加减,复合逻辑与,或,异或,但符号操作如同其他原子类一样,不可设置内存次序。
//后置自加操作,返回原值,自加
testInt = atomicInt++;
//后置自减操作,返回原值,自减
testInt = atomicInt--;
//前置自加,自加并返回更新后的值
testInt = ++atomicInt;
//前置自减,自减并返回更新后的值
testInt = --atomicInt;
//原子合赋值加
atomicInt += 1;
//原子复合赋值减
atomicInt -= 1;
//原子复合赋值与
atomicInt &= 1;
//原子复合赋值或
atomicInt |= 1;
//原子复合赋值异或
atomicInt ^= 1;
以下是全部代码,可进行debug断点调试,熟悉其性质。
#include <atomic>
#include <iostream>
auto main() -> int
{
//初始化整数原子类对象
std::atomic<int> atomicInt(0);
std::atomic<unsigned> atomicUInt(0);
std::atomic<long> atomicLong(0);
//判断是否是无锁结构
std::cout << atomicInt.is_lock_free() << std::endl;
//原子读取,可配合读操作内存次序
int testInt = atomicInt.load();
//原子写入,可配合写操作内存次序
atomicInt.store(1);
//原子交换,返回原值,更新值,可配合读改写操作内存次序
testInt = atomicInt.exchange(2);
//原子比较交换,与期望值(testInt)比较,相同则更新值,否则将值赋值给期望值(testInt)
// weak 版本可能出现虚假失败
//可配合两个内存次序,第一个是读改写(成功更新值),第二个是读(更新值失败)
while (!atomicInt.compare_exchange_weak(testInt, 3))
{
}
//原子比较交换,与weak版的区别是不会出现虚假失败
atomicInt.compare_exchange_strong(testInt, 4);
//原子加法,返回原值,更新完成加操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_add(1);
//原子减法,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_sub(1);
//原子与操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_and(1);
//原子或操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_or(1);
//原子异或操作,返回原值,更新完成操作的值
//可配合读改写内存次序
testInt = atomicInt.fetch_xor(1);
//后置自加操作,返回原值,自加
testInt = atomicInt++;
//后置自减操作,返回原值,自减
testInt = atomicInt--;
//前置自加,自加并返回更新后的值
testInt = ++atomicInt;
//前置自减,自减并返回更新后的值
testInt = --atomicInt;
//原子符合赋值加
atomicInt += 1;
//原子符合赋值减
atomicInt -= 1;
//原子符合赋值与
atomicInt &= 1;
//原子符合赋值或
atomicInt |= 1;
//原子符合赋值异或
atomicInt ^= 1;
return 0;
}
总结
标准整数的原子操作大多可以如同普通整数操作一样方便,只是少了乘除等运算,其他更复杂的操作,直接上锁吧,原子操作一般做不来。
如果是循序学习,标准整数的原子操作只是扩展了部分整数运算,及逻辑运算,新内容并不多,可根据示例仔细了解。