操作手册并发中使用拆分引用计数实现无锁堆栈

Posted

技术标签:

【中文标题】操作手册并发中使用拆分引用计数实现无锁堆栈【英文标题】:Implementation of lock free stack using split reference count in the concurrency in action book 【发布时间】:2017-04-26 15:11:49 【问题描述】:

在 Anthony williams 的 Concurrency in action 书中,他使用拆分引用计数实现了无锁堆栈。在头节点的初始加载之后。这里他使用方法调用increase_head_count,它在下面的代码中列出。

counted_node_ptr old_head = head.load();
increase_head_count(old_head);

void increase_head_count(counted_node_ptr& old_counter)

    counted_node_ptr new_counter;

    do
    
        new_counter = old_counter;
        ++new_counter.external_count;
    
    while (!head.compare_exchange_strong(old_counter, new_counter));

    old_counter.external_count = new_counter.external_count;
  

完整的实现可以在这个链接https://github.com/subjam/concurrency-in-action/blob/master/ch7/stack_ref.cpp找到。

我的问题是,如果出现多个线程尝试同时执行 pop() 并且所有线程都读取头节点,然后只有一个线程执行直到结束,其他线程开始执行此功能之后此实现如何工作的场景。

我在这里被困了很长一段时间,如果有人能帮助我理解这一点,我会很高兴。

【问题讨论】:

【参考方案1】:

如果在 while 子句中,old_counter 仍然等于 new_counter,则 head 设置为 new_counter。任何时候都成功的单线程就是这种情况。

对于同时访问此方法的其他线程, compare_exchange_strong() 返回 false 导致循环迭代。但是,它也会将 head 复制到 old_counter 的内容上。

这意味着在这些其他(不成功的)线程的下一个循环中,old_counter 已更新为 head 的当前内容。

【讨论】:

感谢您的回答..我对此没有什么疑问..说两个线程来执行 increase_head_count 函数,一个在开始时被挂起。现在一个线程将一直执行,甚至删除 old_node 指针(参见链接中给出的完整实现)现在当挂起的线程唤醒时,它不会抛出异常(无效内存或类似的东西) ) 执行 ++new_counter.external_count 时;代码,因为它引用了已删除的节点(通过第一个线程)。 第一个线程(一直通过方法 increase_head_count() )将在删除之前控制 head。它会在删除之前尝试将其从堆栈中删除。当它成功从堆栈中移除时,第二个线程(在 increase_head_count() 中唤醒)将没有任何对 head 的引用。正如我上面所说,old_counter 现在将更新为指向新的head,因此它不会尝试访问任何已删除的节点。 我对这段代码的担忧是,如果第一个(成功的)线程获得了最后一个推送节点的控制权,并且head 现在指向原始counted_node_ptr,代码(在方法@第二个线程的 987654328@) 不会尝试删除此 head,因为 ptrNULL。不删除头部将允许在counted_node_ptr 内循环的所有辅助线程离开pop 返回一个空值T。但是 ptr 的原始值 head 不能保证是 NULL,因为没有构造函数它不会被初始化。

以上是关于操作手册并发中使用拆分引用计数实现无锁堆栈的主要内容,如果未能解决你的问题,请参考以下文章

C++并发编程----无锁实现线程安全队列(《C++ Concurrency in Action》 读书笔记)

JVM并发的可达性分析

内存管理

常用的垃圾回收算法

C++ String的引用计数写时复制 的实现 《More Effective C++》

C++ String的引用计数写时复制 的实现 《More Effective C++》