C++并发编程(十七)
前言
std::atomic<Ptr*> 是原子指针类,除了和原子布尔类相同的成员函数,还有更多的计算相关函数,如果对前两篇文章完全掌握,此篇文章的内容不难掌握。
一、操作std::atomic<Ptr*>类对象
与原子布尔类一样,原子指针类可用普通的类型指针进行初始化:
char charArr[] = "abcd";
//初始化原子指针
std::atomic<char *> atomicPtr(charArr);
判断是否无锁结构:
//原子指针是否是无锁结构
std::cout << atomicPtr.is_lock_free() << std::endl;
原子指针值的读取与写入:
//读取原子指针值,可用读操作的内存次序
auto charPtr = atomicPtr.load();
char charArr2[] = "efgh";
//写入原子指针,可用写操作的内存次序
atomicPtr.store(charArr2);
原子指针值的交换更新:
//更新原子指针,返回原值,读改写内存次序
charPtr = atomicPtr.exchange(charArr);
原子指针值的 “比较替换更新”:
char *charPtr2 = charArr2;
char charArr3[] = "ijkl";
//第一个参数必须是指针,不能是数组,因参数是引用,不会退化成指针
//依然是两个内存操作次序,与期望指针值一致,则是读改写次序,否则是读次序
while (!atomicPtr.compare_exchange_weak(charPtr2, charArr3))
{
}
//同 weak 版本,第一个参数必须是指针
//依然是两个内存操作次序
atomicPtr.compare_exchange_strong(charPtr2, charArr);
原子指针增加了算数函数,
后移 fetch_add(ptrdiff_t __op, std::memory_order __m = memory_order_seq_cst)
前移fetch_sub(ptrdiff_t __op, std::memory_order __m = memory_order_seq_cst)
其语义是返回原值,指针前后移动若干,可按照 “读改写” 操作设置内存顺序。
//返回指针原值,原子指针向后移动一个,读改写操作,可用任何内存次序
charPtr = atomicPtr.fetch_add(1);
//返回指针原值,原子指针向前移动一个,读改写操作,可用任何内存次序
charPtr = atomicPtr.fetch_sub(1);
原子指针类还有自增 “++”,自减 “–” 操作,和普通指针运算操作无异,因原子指针类不支持拷贝赋值,只会返回普通指针,并且不可设置内存次序。
//原子指针向后移动一个,并返回更新的值,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = ++atomicPtr;
//原子指针向前移动一个,并返回更新的值,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = --atomicPtr;
//返回指针原值,原子指针后移一个,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = atomicPtr++;
//返回指针原值,原子指针前移一个,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = atomicPtr--;
//原子指针像后移动一个,不可设定内存次序,服从std::memory_order_seq_cst
atomicPtr += 1;
//原子指针向前移动一个,不可设定内存次序,服从std::memory_order_seq_cst
atomicPtr -= 1;
以下是全部示例代码,可一边 debug 一边熟悉其操作。
#include <atomic>
#include <iostream>
auto main() -> int
{
char charArr[] = "abcd";
//初始化原子指针
std::atomic<char *> atomicPtr(charArr);
//原子指针是否是无锁结构
std::cout << atomicPtr.is_lock_free() << std::endl;
//读取原子指针值,可用读操作的内存次序
auto charPtr = atomicPtr.load();
char charArr2[] = "efgh";
//写入原子指针,可用写操作的内存次序
atomicPtr.store(charArr2);
//更新原子指针,返回原值,读改写内存次序
charPtr = atomicPtr.exchange(charArr);
char *charPtr2 = charArr2;
char charArr3[] = "ijkl";
//第一个参数必须是指针,不能是数组,因参数是引用,不会退化成指针
//依然是两个内存操作次序
while (!atomicPtr.compare_exchange_weak(charPtr2, charArr3))
{
}
//同 weak 版本,第一个参数必须是指针
//依然是两个内存操作次序
atomicPtr.compare_exchange_strong(charPtr2, charArr);
//返回指针原值,原子指针向后移动一个,读改写操作,可用任何内存次序
charPtr = atomicPtr.fetch_add(1);
//返回指针原值,原子指针向前移动一个,读改写操作,可用任何内存次序
charPtr = atomicPtr.fetch_sub(1);
//原子指针向后移动一个,并返回更新的值,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = ++atomicPtr;
//原子指针向前移动一个,并返回更新的值,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = --atomicPtr;
//返回指针原值,原子指针后移一个,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = atomicPtr++;
//返回指针原值,原子指针前移一个,不可设定内存次序,服从std::memory_order_seq_cst
charPtr = atomicPtr--;
//原子指针像后移动一个,不可设定内存次序,服从std::memory_order_seq_cst
atomicPtr += 1;
//原子指针向前移动一个,不可设定内存次序,服从std::memory_order_seq_cst
atomicPtr -= 1;
return 0;
}
总结
原子指针类相较原子布尔类,增加了一些运算操作,及重载运算符,不难理解,多用即会。