如何在无锁并发队列中实现“Front”方法?

Posted

技术标签:

【中文标题】如何在无锁并发队列中实现“Front”方法?【英文标题】:How to implement "Front" method in a lock-free concurrent queue? 【发布时间】:2019-11-25 23:54:18 【问题描述】:

我正在尝试实现一个并发无锁队列。我正在密切关注这篇论文:https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf 但是本文没有提供公开 Front() 或 Back() 方法的安全方法。 Enqueue() 和 Dequeue() 方法在那里。 (本文详细阐述了无锁版本和带锁版本,我对前者感兴趣)。让我现在直截了当地提出问题。在所有安全检查(类似于 Enqueue)之后,Front() 函数的紧缩归结为:

1. if(local_copy_of_head == curr_shared_head)   // Let's say I read this atomically. The equality means the head didn't move since the time I copied.
2.   return local_copy_of_head;
3. 

我想明确一点,虽然这是一个关于如何实现 Front() 函数的问题,但我主要感兴趣的是如何将上面的 sn-p 重构为一个没有任何锁的原子块。 我担心的是,如果 Thread1 在执行第 1 行后被中断(假设我以原子方式阅读),那么第二个线程清空队列,Thread1 被重新调度并返回一个陈旧的结果。

【问题讨论】:

【参考方案1】:

你不能。你可以制作一个可以运行很多次的糟糕版本,但总的来说是失败的。要揭开无锁的神秘面纱,请将其替换为之前的术语忙等待。 Busy-waiting更恰当地捕捉了消耗cpu资源的选择,希望能恰当地检测到你的race end condition,因此结果是一致的。

通过放弃队列的头部(Front),您不知道这个客户端在承诺吸收它之前可能会使用它多长时间(Dequeue,原文如此:这不是 dequeue 的意思)。因此,您的 Front 函数只能是设计不佳的队列实现的前端。

从来都不是很长的时间。好吧,不是从来没有,但是,它需要一个伴随函数(Absorb),它可以告诉我们这个队列头的吸收是队列头上唯一的操作,因为相应的 Front 被调用了;因此队列处于相同状态。如果不是,则必须向调用者返回一个失败,其中它必须回滚正在进行的任何操作以反映此失败。

好吧,所以不是从来没有,但是已经完成的只是将事务语义提升了一个级别,向设计师暗示设计可能有点缺乏。所以你有一个无事务队列。我们中的一些人曾经称其为增量

【讨论】:

以上是关于如何在无锁并发队列中实现“Front”方法?的主要内容,如果未能解决你的问题,请参考以下文章

Linux(程序设计):24---无锁CAS(附无锁队列的实现)

高性能无锁并发框架Disruptor,太强了

如何在 C 中实现无锁共享标志?

如何在 Java EE 环境中实现请求限制?

无锁队列的C代码

在无锁实现中没有互斥锁的条件变量