具有移动到尾部功能的无锁队列算法

时间:2012-09-11 11:56:24

标签: algorithm queue atomic lock-free lru

对于LRU缓存,我需要一个无锁队列的算法,类似于论文Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms中描述的那个

但是为了维护LRU队列,我还需要删除队列中的节点(尾部的节点除外)。

我的想法是只使用CAS操作将节点标记为已删除,然后使用单个清理线程,稍后将从队列中删除已删除的节点。

我发现创建无锁算法比我最初预期的要复杂得多。所以,我的问题是:
是否有可用的此类算法?

这是我目前使用的结构:

一般

  • 节点具有以下结构:struct {e * entry,next pointer,prev pointer,state uint32}
  • 为了避免为每个新节点分配多个内存,请分配一组节点。
  • 指向节点的指针由数组索引值和更新计数器组成,复用到单个uint64
  • 节点状态由高位组成,告知节点是否被删除。其余位用作更新计数器

入队

  • 辅助队列保存未使用节点的列表,并通过从aux队列出队获取“新”节点,然后设置为默认值
  • node.prev在新节点入队之前设置为当前尾部

出列

  • head.next.prev在头部出列之前是CAS的NIL值。如果head.next.prev设置为CLEANUP(由清理线程处理),则不允许出列,并且线程将产生CPU并重新开始。
  • 成功出列具有未删除状态的节点后,将删除CAS状态,并将该节点返回到辅助队列。在失败(或状态已设置为已删除)时,出列的node.prev将从NIL更改为CLEANUP,向清理线程发信号通知该节点已出列。然后,dequeue将重新开始,直到一个未删除的节点成功出列并且CAS被删除。

删除

  • 要标记队列内删除,请将状态CAS删除并在成功时传递到清理队列。在失败时没有做任何事情,但函数返回。

清理帖子

  • 如果node.prev是CLEANUP,则Dequeue已将其从队列中删除。节点被传递回辅助队列。
  • 如果node.prev为NIL,则该节点即将变为head,head或即将出列。如果node == head,则清理线程尝试执行出队(状态已更改为已删除)。失败时,清理过程将全部开始。
  • 如果node设置为另一个节点,则node.prev为CAS'ed到CLEANUP。如果head.next == node,则可以防止出现任何出队。成功时,使用双链表进行队列内删除。失败时,清理过程将全部开始。

Node.prev用于告诉:

  • 队列中的前一个节点
  • 节点即将成为首脑
  • 清理线程正在处理节点
  • 节点已出列

1 个答案:

答案 0 :(得分:3)

队列元素的逻辑删除实际上是有问题的,这就是为什么你不会为此找到论文的原因。此外,它是一个非常具体的功能添加到一个非常通用的数据结构,这是你找不到论文的另一个原因;你只会找到一般数据结构的论文。

问题是双重的;首先,队列通常不支持游标。其次,知道访问您希望逻辑删除的元素是否仍然安全 - 例如,它可能已经出列并取消分配吗?

您引用的队列使用指针 - 计数器对来解决ABA,这意味着使用空闲列表。在这种情况下,您始终可以确保元素尚未被释放。

关于游标,您需要在出列处输入,然后沿着队列继续进入入队指针。但是,如果您正在查看的元素在进入下一个元素之前出现了会发生什么?然后,您将跟随已从队列中删除并位于空闲列表中的元素的下一个指针。实际上,一般情况下,任何都可能发生在从一个元素移动到另一个元素的光标之间的队列中 - 包括完全删除队列和使用不同元素进行重新创建。

因此,您需要一个链接列表,它明确支持游标。

你没有提到你正在使用的语言?