标准::原子::compare_exchange与两个memory_order参数一起使用的真实示例
Real-world example where std::atomic::compare_exchange used with two memory_order parameters
您能否举一个真实世界的例子,其中出于某种原因使用了std::atomic::compare_exchange
的两个memory_order参数版本(因此memory_order参数版本的一个是不够的(?
在许多情况下,compare_exchange
上的第二个内存排序参数设置为memory_order_relaxed
。在这些情况下,省略它通常没有错,只是效率可能降低。
下面是一个简单的、无锁的列表/堆栈示例,它需要在compare_exchange_weak
上安装第二个不同的排序参数才能实现无数据争用。
对push
的调用可以并发执行,但为了避免无锁数据操作的复杂性, 假设在执行对push
的调用时无法从堆栈中删除节点;即避免悬空指针。
template<typename T>
class mystack {
struct node {
node *next = nullptr;
T data;
int id;
node(int id) : id{id} { }
};
std::atomic<node *> head{nullptr};
public:
void push(T data, int id);
bool pop(T &data); // not implemented
};
template<typename T>
void mystack<T>::push(T data, int id)
{
node *newnode = new node{id};
newnode->data = std::move(data);
node *current_head = head.load(std::memory_order_relaxed); // A
for (;;)
{
newnode->next = current_head;
if (head.compare_exchange_weak(current_head, newnode,
std::memory_order_release, // B
std::memory_order_acquire)) // C
{
/*
* 'current_head' may not be derefenced here since the initial load (at A)
* does not order memory 'current_head' is pointing at.
*
* a release barrier (at B) is necessary to make 'newnode' available
* to other threads
*/
std::cout << "Insertion successfuln";
break;
} else
{
/*
* 'current_head' is the updated head pointer after 'compare_exchange' failed
* Since it was inserted by another thread (the CAS failed),
* an acquire barrier must be set (at C) in order to be able to access data
* 'current_head' is pointing at.
*/
std::cout << "Insertion failed after head changed to id: " <<
current_head->id << std::endl;
}
}
}
在push
中,初始load
(在 A 处(是一个松弛操作,这意味着即使head
指针是原子加载的, 它可能不会被取消引用,因为它引用的内存在此线程中是无序的。
如果compare_exchange_weak
返回成功,newnode
将插入列表的头部,并通过设置释放屏障(在 B 处(提供给其他线程。 另一个访问此数据的线程(稍后通过pop
(需要设置获取屏障。
如果compare_exchange_weak
返回失败(虚假地忘记(,另一个线程刚刚插入一个新的node
实例,current_head
更新为新值head
。 由于current_head
现在指向在另一个线程中分配和释放的数据,因此如果要取消引用current_head
,则需要获取屏障。
这是正确的,因为cout
失败消息包含current_head->id
。
如果省略最后一个参数,则第一个屏障参数将用于故障load
场景,但由于这是一个释放屏障, 有效屏障会衰减到memory_order_relaxed
,导致current_head->id
上的数据竞争。
- 如何将enable-if与模板参数和参数包一起使用
- 如何使我的 sizeof sum 结构与空参数包一起工作
- 将 out/in out 参数与 if/switch 的 init 语句一起使用
- 概念可以与模板模板参数一起使用吗?
- 如何在Visual Studio 2017上将类方法设置为参数并将它们与lambda一起使用?
- 为什么当我尝试将priority_queue与参数一起使用作为指向结构的指针时会弹出错误
- 标准::原子::compare_exchange与两个memory_order参数一起使用的真实示例
- 如何在 C++ 中将 typedef 与类初始值设定项参数一起使用?
- 我可以将"token pasting operator"与"const"模板参数一起使用吗?
- 如何将可选参数与基于 QAxFactory 的 API 一起使用
- 哪个强制转换应与模板类参数一起使用,dynamic_cast或reinterpet_cast?
- 使用 std::vector<Particle> 粒子;函数 .at() 不能与迭代器一起使用,它作为 for 循环中的参数
- 有没有一种干净(更)的方法将 CRTP 与可变参数继承混合在一起?
- 将 auto 与生成元组的可变参数模板一起使用
- 将 boost::function 与指向派生类的共享指针的参数一起使用
- 当没有显式关键字与单参数构造函数一起使用时,编译器可以发出警告
- 类模板参数扣除是否可与STD :: MAP一起使用
- 当将 getline 与 int 一起使用时,如何修复"没有重载函数 'getline' 的实例与参数列表匹配"
- 如果与 QProcess 一起传递,则无法识别参数
- 如何将 #ifndef 与宏参数一起使用