Delphi中是否存在“多生产者-单消费者”的无锁队列?

13

我找到了几个单生产者-单消费者的实现,但没有找到多生产者-单消费者的实现。

在Delphi中是否存在“多生产者-单消费者”的无锁队列?


1
一个非常有趣的回答,关于使用无锁算法和替代方案进行性能调优:https://dev59.com/p3RA5IYBdhLWcg3wtwWe#853510。 - mghie
4个回答

5
来自OmniThreadLibrary的无锁队列支持多个生产者。您可以将其与线程库分开使用(即您可以在任何其他框架中使用OtlContainers单元)。
正如Daniele在下面指出的那样,OmniThreadLibrary中有两个队列。OtlContainers中的一个支持多个生产者和多个消费者,而OtlComm中的“更智能”的版本(只是简单版本的包装器)仅支持单个生产者/单个消费者。
OmniThreadLibrary项目的文档仍然是一个大问题 :(。关于队列的一些信息可以在此处找到。

真的吗?我使用了你的List,但在源代码中有一个“悲伤”的注释针对我... "{:无锁、单写、单读环形缓冲区。 } IOmniQueue = interface['{AE6454A2-CDB4-43EE-9F1B-5A7307593EE9}']"你说OmniQueue支持多生产者,单消费者? - Daniele Teti
抱歉,我的错误。OtlComm中的“高级”队列是单生产者/单消费者。OtlContainers中的“低级”队列是多生产者/多消费者。因此,如果您想使用多个生产者,则必须使用队列对象的简单变体。我已经更正了上面的文本以引用正确的单位名称。 - gabr

3

+1。请注意,如果应用程序需要在早期的Windows系统上运行,则必须实现另一种方法。同时,请注意,没有简单的方法让消费者在空队列上阻塞。 - mghie
SList函数生成的是栈,而不是队列。 - Rob Kennedy
2
@Rob Kennedy:并不完全正确,如果消费者使用InterlockedFlushSList()而不是InterlockedPopEntrySList(),则可以自由地在两个方向上处理列表项。 - mghie
@mghie:这正是我们使用的确切方法(push和flush)来执行无锁MPSC队列。 - Adisak

2

http://svn.berlios.de/svnroot/repos/dzchart/utilities/dzLib/trunk/lockfree/

@Daniele Teti:

读者必须等待所有仍然可以访问旧队列的写入者退出Enqueue方法。由于读者在Dequeue方法中首先要做的事情是为新进入Enqueue的写入者提供一个新的队列,因此所有引用旧队列的写入者退出Enqueue方法不应该需要太长时间。但你是对的:它只是针对写入者无锁,但可能仍需要读取线程等待某些写入者退出Enqueue。


1
我在尝试使用这个列表,但是...代码注释对于一个"无锁"的列表来说有些奇怪:" // 不幸的是,其他线程仍然可能持有旧队列的引用。 // 为了100%确定,我们需要等待ActiveWriters计数下降到0 // 如果当前有写入者,我们会等待事件被设置, // 这将由第一个将ActiveWriters减少到0的写入者设置。 如果没有,则无需等待。" 这似乎是一种“等待”同步方法... 我错了吗? (我发现这些注释位于TMultiWriteSingleReadLockFreeQueue.Dequeue函数中) - Daniele Teti

2
对于一个多生产者/单消费者的队列/先进先出(FIFO)结构,您可以使用SLIST或简单的无锁LIFO堆栈轻松地制作一个无锁队列。您需要做的是为消费者创建第二个“私有”堆栈(也可以使用SLIST来简化或选择任何其他堆栈模型)。消费者从私有堆栈中弹出项目。每当私有LIFO用尽时,您需要进行刷新而不是从共享并发SLIST中弹出(获取整个SLIST链),然后按顺序遍历已刷新的列表,将项目推入私有堆栈中。
这适用于单生产者/单消费者和多生产者/单消费者情况。
但是,对于多生产者/多消费者情况,它不起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接