use*_*333 9 c++ concurrency multithreading lock-free data-structures
在 C++ Concurrency in Action, 2e 中,作者描述了一个无锁线程安全的链表实现。现在,他正在描述 pop() 方法以及如何以一种类似于“垃圾收集器”的方法安全地删除节点,以确保没有其他线程在同一实例上调用 pop。这是 pop 的一些代码:
#include <atomic>
#include <memory>
template<typename T>
class lock_free_stack
{
private:
std::atomic<unsigned> threads_in_pop;
void try_reclaim(node* old_head);
public:
std::shared_ptr<T> pop()
{
++threads_in_pop;
node* old_head=head.load();
while(old_head &&
!head.compare_exchange_weak(old_head,old_head->next));
std::shared_ptr<T> res;
if(old_head)
{
res.swap(old_head->data);
}
try_reclaim(old_head);
return res;
}
};
Run Code Online (Sandbox Code Playgroud)
重要的是,每次调用 pop() 时,计数器都会自动递增。然后,try_reclaim 函数将递减所述计数器。这是 try_reclaim 的实现:
void try_reclaim(node* old_head)
{
if(threads_in_pop==1) //#1
{
node* nodes_to_delete=to_be_deleted.exchange(nullptr);
if(!--threads_in_pop) //#2
{
delete_nodes(nodes_to_delete);
}
else if(nodes_to_delete)
{
chain_pending_nodes(nodes_to_delete);//#3
}
delete old_head; //#4 THIS IS THE PART I AM CONFUSED ABOUT
}
else
{
chain_pending_node(old_head);
--threads_in_pop;
}
}
Run Code Online (Sandbox Code Playgroud)
此处调用的其他函数的实现无关紧要(它们只是将节点添加到要删除的节点链中),因此我省略了它们。我在代码中感到困惑的部分是#4(标记)。这里作者在传入的old_head上调用delete。但是,为什么在删除old_head之前不检查此时threads_in_pop是否仍然为零呢?他在第 2 行和第 1 行中再次检查以确保另一个线程当前不在 pop() 中,那么为什么在继续删除 old_head 之前他不再次检查?另一个线程不能在#3 之后立即调用pop(),从而增加计数器,并且当第一个线程到达#4 时,threads_in_pop 将不再为零?
也就是说,当代码到达#4 时,threads_in_pop 是不是有可能是例如2?在这种情况下,他怎么能安全地删除 old_head 呢?有人可以解释一下吗?
调用的线程try_reclaim
刚刚old_head
从堆栈中删除。
该类确保 的任何其他使用都old_head
必须在pop
其他线程的调用内部,因此如果该线程发现没有其他并发调用,那么它就知道它是该指针的独占持有者old_head
。然后,只要它不发布该指针以便可以从另一个线程中获取它,它就可以在它访问该指针时将其删除。
所以实施是安全的。你问的问题:“他为什么不[再次]检查”表明你的想法是错误的。再次检查并不能证明任何事情,因为如果另一个线程有可能进入pop
并使用old_head
,那么它总是可能在您检查后发生!
归档时间: |
|
查看次数: |
266 次 |
最近记录: |