如何构建无锁队列?

时间:2009-11-12 19:13:16

标签: language-agnostic queue atomic lockless

我今天花了很多时间来研究无锁队列。我有一个多生产者,多个消费者的情况。为了测试,我使用Win32下的Interlocked SList实现了一个系统,它使我基于线程的高度基于任务的代码的性能翻了一番。不幸的是,我希望支持多个平台。在多个平台上联锁本身不是问题,我可以安全地假设我可以毫无问题地互锁。然而,实际的实施失去了我。

最大的问题似乎是您需要保证列表推送/弹出只使用一个互锁呼叫。否则你会留下空间让另一个线程夹入并搞砸了。我不确定微软的实现如何在幕后工作,并希望了解更多。

有人能指出我有用的信息(平台和语言非常相关)吗?

除此之外,我很想知道它是否可以实现无锁向量。这对我来说有很大的用处:) 干杯!

编辑:阅读了草药的DDJ文章后,我可以看到一个减少的锁定队列,它与我已经拥有的非常相似。但是我注意到最后有一些文件可以使用双重比较和交换(DCAS)操作进行真正的无锁队列。有没有人使用cmpxchg8b(或cmpxchg16b)实现队列?

我只是在思考这一点(没有阅读论文),但你可以使用这个系统同时更新头部和尾部指针,从而避免在2个原子操作之间跳过另一个线程的任何问题。但是你仍然需要获得下一个头指针来测试尾部指针,看看你是否刚刚修改了尾部。当另一个线程准备这样做时,你如何避免另一个线程更改此信息?这是如何以无锁方式实现的?或者我最好还是阅读一篇研究论文的难以理解的内容? ;)

5 个答案:

答案 0 :(得分:11)

你可能会以最小的难度实现一个有限大小的队列......我最近在想这个并想出了这个设计,但你可能会发现许多其他有趣的想法:(警告:它可能有一些问题! )

  • 队列是指向项目的指针数组
  • 你必须管理2个指针(head,tail),它们以与循环缓冲区相同的方式在队列中工作
  • 如果head == tail,则没有商品
  • 如果您想要enqueue(ptr),则互锁交换tail为NULL(prev_tail为交换值)
    • 如果prev_tail == NULL,请再试一次
    • 如果prev_tail + 1(包围)== head,您的队列已满
    • 否则将ptr放入*prev_tail并将prev_tail+1分配给tail(注意缓冲区环绕)
  • for dequeue()制作副本tmp_head = head并检查tmp_head == tail
    • 如果是,则返回,因为队列为空
    • 如果它是假的
      • *tmp_head保存为ptr
      • 执行CAS:将headtmp_head交换headhead+1进行比较
      • 如果CAS失败 - 通过
      • 启动整个功能
      • 如果成功 - 返回ptr

您可以等待头部和尾部CAS操作,但如果队列没有争用,您应该第一次成功,没有不必要的锁定。

无限大小的队列“有点”难度;)但是你应该能够为大多数需求创建一个足够大的队列。

答案 1 :(得分:3)

我认为这个主题有一些有趣的讨论here,特别是this thread.

答案 2 :(得分:3)

您可能想看看Herb Sutters实现低锁定队列。

http://www.drdobbs.com/hpc-high-performance-computing/211601363

它确实使用了c ++ 0x atomics但它(应该)很容易用你的特定体系结构原子操作实现(__sync_ *使用GNU,solaris *在solaris等上)。

答案 3 :(得分:2)

These伙计们,也许你可以在那里找到一些灵感。其他有趣的文件是yqueue.hpp和atomic_ptr.hpp

答案 4 :(得分:1)

viraptor解决方案是锁定,没有多个生产者/多个消费者无锁队列算法我知道。