为什么这个无锁堆栈中的 'deleting' 个节点 class 会导致竞争条件?
Why would 'deleting' nodes in this lock-free stack class would cause race condition?
在 Anthony Williams 的 "C++ Concurrency in Action" 一书中,第 7.2.1 节列出了一个无锁堆栈实现:
template <typename T>
class lock_free_stack {
struct node {
shared_ptr<T> data_;
node* next_;
node(const T& data) : data_(make_shared(data)) {}
};
atomic<node*> head_;
public:
void push(const T& data)
{
node* new_node = new node(data);
new_node->next_ = head_.load();
while(!head.compare_exchange_weak(new_node->next_, new_node));
}
shared_ptr<T> pop()
{
node* old_head = head_.load();
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_));
return old_head ? old_head->data_ : shared_ptr<T>();
}
};
然后在第 7.2.2 节中,作者说“......在 pop() 中,我们选择泄漏节点以避免一个线程删除一个节点而另一个线程仍然持有指向的指针的竞争条件它即将取消引用。"
1) 我不明白为什么会发生这种情况以及为什么以下 pop() 函数会导致竞争条件:
shared_ptr<T> pop()
{
node* old_head = head_.load(); // (1)
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_)); // (2)
shared_ptr<T> res; // (3)
if (old_head) {
res.swap(old_head->data);
delete old_head;
return res;
} else {
return {};
}
}
2)为什么多个线程同时调用pop(),第(3)行后'old_head'变量可以指向同一个节点对象?
线程 1 继续执行 (2)。它开始评估 head_->next
。它将 head_
加载到寄存器中,然后放弃优先级。
线程2从函数的开始执行到结束。它通过删除 head_
和 returns head_
.
的内容来将其从存在中移除
线程 1 苏醒。它在获取 ->next
字段的寄存器中跟在 head_
之后。但是线程2已经删除了head_
指向的数据,我们只是跟着一个悬空指针
我在阅读时也有同样的困惑,并试图 google 答案...我找不到答案,最后去查看 compare_exchange_weak 参考资料。
我们缺少的部分是您传入第二个所需参数的时间,您已经取消引用了悬垂指针...
你不能真正摆脱它,因为函数需要知道你传递的是什么,从而取消引用它。
在 Anthony Williams 的 "C++ Concurrency in Action" 一书中,第 7.2.1 节列出了一个无锁堆栈实现:
template <typename T>
class lock_free_stack {
struct node {
shared_ptr<T> data_;
node* next_;
node(const T& data) : data_(make_shared(data)) {}
};
atomic<node*> head_;
public:
void push(const T& data)
{
node* new_node = new node(data);
new_node->next_ = head_.load();
while(!head.compare_exchange_weak(new_node->next_, new_node));
}
shared_ptr<T> pop()
{
node* old_head = head_.load();
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_));
return old_head ? old_head->data_ : shared_ptr<T>();
}
};
然后在第 7.2.2 节中,作者说“......在 pop() 中,我们选择泄漏节点以避免一个线程删除一个节点而另一个线程仍然持有指向的指针的竞争条件它即将取消引用。"
1) 我不明白为什么会发生这种情况以及为什么以下 pop() 函数会导致竞争条件:
shared_ptr<T> pop()
{
node* old_head = head_.load(); // (1)
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_)); // (2)
shared_ptr<T> res; // (3)
if (old_head) {
res.swap(old_head->data);
delete old_head;
return res;
} else {
return {};
}
}
2)为什么多个线程同时调用pop(),第(3)行后'old_head'变量可以指向同一个节点对象?
线程 1 继续执行 (2)。它开始评估 head_->next
。它将 head_
加载到寄存器中,然后放弃优先级。
线程2从函数的开始执行到结束。它通过删除 head_
和 returns head_
.
线程 1 苏醒。它在获取 ->next
字段的寄存器中跟在 head_
之后。但是线程2已经删除了head_
指向的数据,我们只是跟着一个悬空指针
我在阅读时也有同样的困惑,并试图 google 答案...我找不到答案,最后去查看 compare_exchange_weak 参考资料。 我们缺少的部分是您传入第二个所需参数的时间,您已经取消引用了悬垂指针... 你不能真正摆脱它,因为函数需要知道你传递的是什么,从而取消引用它。