test_and_set线程的这种用法是否安全?

时间:2010-05-24 20:54:34

标签: c++ c gcc lock-free

我一直在考虑如何实现一个无锁的单链表。说实话,我没有看到很多防弹方法来做到这一点。即使使用CAS使用push的某种程度的ABA problem更强大的方式也会出现。

所以我开始思考。部分无锁系统不会比总是使用锁更好吗?有些操作可以是原子的,也可以是无锁的吗?如果我能做到这一点,它应该仍然是线程安全的。

所以,关于这个问题。我在考虑一个简单的单链表。 2主要业务。 poppushvoid push(int n) { T *p = new T; p->n = n; p->next = root; root = p; } 总是插在前面。像这样:

T *pop() {
    T *p = root;
    root = root->next;
    return p;
}

pop总是占据第一个元素。像这样:

T *pop() {
    return __sync_lock_test_and_set(&root, root->next);
}

显然,推送非常简单,可能不会发生简单的无锁方法。但流行看起来可能是可行的。使用gcc-intrinsics我已经想到了这个:

test_and_set

功能相同?对。无锁?对。线程安全? 我不知道。我的直觉反应是否定的,这就是原因。

我担心root->next的一个参数必须取消引用内存。如果根目录在__sync_lock_test_and_set和对T *pop() { T *temp = root->next; // are we broken if a push/pop happens here? return __sync_lock_test_and_set(&root, temp); } 的调用之间发生变化,该怎么办?

我认为这段代码与此相同:

{{1}}

所以,就像我说的,我认为这段代码不正确。但是,任何人都可以肯定地说我得出了正确的结论(我讨厌写出一些效果很好的东西)。如果我怀疑它确实被打破了。有没有简单的解决方案?

3 个答案:

答案 0 :(得分:2)

你是对的。在C ++中,函数的参数以任何顺序进行求值,但当然你的编译器无法知道root->next是序列中的原子操作。

考虑调用pop()的两个线程:一个线程评估root->next,然后另一个线程评估root->next,并且都调用test_and_set()。现在你只弹出一个节点。

答案 1 :(得分:1)

两件事:(1)test& set只有2的共识数;对于这种弱同步原语,只需使用读/写存储器阻塞而不会产生专用指令的开销就足够了; (2)ABA问题是一个真正的问题,很少有解决方案;但是,使用CAS(32位系统上的cmpxchg8b和x86 / -64的64位系统上的cmpxchg16b),寄存器上部有足够的空间来存储ABA在实践中从未发生的如此大的时间戳(在相当人为的设置中,它需要一个线程停止几天或几周,然后在正确的时刻唤醒)。

我认为,您正在尝试实现无锁队列(而不是列表)。队列比列表更容易实现。由Edya Lazan-Mozes和Nir Shavit撰写的论文“对无锁FIFO队列的乐观方法”以及Maged M. Michael和Michael L. Scott的“简单,快速,实用的非阻塞和阻塞并发队列算法”均为非常有用且易于实现无锁队列的方法。

但是,如果你坚持使用无锁链接列表,请考虑Michail Fomitchev和Eric Ruppert在“无锁链接列表和跳过列表”中的实现。您还可以查看Damian Dechev的无锁动态数组(维基百科有一个链接)。

答案 2 :(得分:0)

pop的两个版本中:

T *pop() {
    T *p = root;
    root = root->next;
    return p;
}

T *pop() {
    return __sync_lock_test_and_set(&root, root->next);
}

你有一个错误,就是你没有在从假定的根节点读取之前验证你的列表/堆栈是否为空。

这就解决了你提到的问题,即在test_and_set发生之前必须解除引用root以获得下一个问题。它基本上变成了test_and_then_test_and_set操作,其中and_then表示需要多个步骤。

您的第一个版本的pop必须是:

T *pop() {
    T *p = root;
    if (root) {
        root = root->next;
    }
    return p;
}

并且我相信你可以看到这会在混合中加入更多的步骤。