Go中的原子比较和交换结构

时间:2012-07-17 15:03:59

标签: struct queue go compare-and-swap

我正在尝试使用Maged M. Michael和Michael L. Scott的算法为并发应用创建一个非阻塞队列包,如here所述。

这需要使用由"sync/atomic"包提供的原子CompareAndSwap 但是我不确定以下伪代码的Go等价物是什么:

E9:   if CAS(&tail.ptr->next, next, <node, next.count+1>)

其中tailnext的类型为:

type pointer_t struct {
    ptr   *node_t
    count uint
}

node的类型为:

type node_t struct {
    value interface{}
    next  pointer_t
}

如果我理解正确,似乎我需要用结构(指针和uint)来做CAS。这可能是atomic - 包吗?

感谢您的帮助!

2 个答案:

答案 0 :(得分:10)

  

如果我理解正确,似乎我需要使用结构(包括&gt;指针和uint)来执行CAS。甚至可以使用原子包吗?

不,那是不可能的。大多数架构仅支持单个单词的原子操作。然而,许多学术论文使用了今天无法获得的更强大的CAS语句(例如比较和交换双重)。幸运的是,在这种情况下通常会使用一些技巧:

  • 例如,您可以从指针中窃取几个位(特别是在64位系统上)并使用它们对您的计数器进行编码。然后你可以简单地使用Go的CompareAndSwapPointer,但是在尝试取消引用之前你需要屏蔽指针的相关位。

  • 另一种可能性是使用指向你的(immutable!)pointer_t结构的指针。无论何时想要修改pointer_t结构中的元素,都必须创建副本,修改副本并原子地替换指向结构的指针。这个成语称为COW(写入时复制),适用于任意大型结构。如果您想使用此技术,则必须将下一个属性更改为next *pointer_t

由于教育原因,我最近在Go写了一个无锁列表。您可以在此处找到(imho记录完善的)来源:https://github.com/tux21b/goco/blob/master/list.go

这个相当简短的示例过度使用atomic.CompareAndSwapPointer并且还为标记指针引入了原子类型(MarkAndRef结构)。这种类型与你的pointer_t结构非常相似(除了它存储bool +指针而不是int +指针)。它用于确保在您尝试直接插入元素时未将节点标记为已删除。随意使用此源作为您自己项目的起点。

答案 1 :(得分:0)

您可以这样做:

 if atomic.CompareAndSwapPointer(
    (*unsafe.Pointer)(unsafe.Pointer(tail.ptr.next)), 
    unsafe.Pointer(&next), 
    unsafe.Pointer(&pointer_t{&node, next.count + 1})
 )