C 我的自旋锁实现正确且最优吗?
我用旋转锁来保护一个非常小的关键部分。争用很少发生,因此自旋锁比常规互斥锁更合适 我当前的代码如下所示,并假定为x86和GCC:C 我的自旋锁实现正确且最优吗?,c,multithreading,concurrency,mutual-exclusion,spinlock,C,Multithreading,Concurrency,Mutual Exclusion,Spinlock,我用旋转锁来保护一个非常小的关键部分。争用很少发生,因此自旋锁比常规互斥锁更合适 我当前的代码如下所示,并假定为x86和GCC: volatile int exclusion = 0; void lock() { while (__sync_lock_test_and_set(&exclusion, 1)) { // Do nothing. This GCC builtin instruction // ensures memory barrie
volatile int exclusion = 0;
void lock() {
while (__sync_lock_test_and_set(&exclusion, 1)) {
// Do nothing. This GCC builtin instruction
// ensures memory barrier.
}
}
void unlock() {
__sync_synchronize(); // Memory barrier.
exclusion = 0;
}
所以我想知道:
* Is it correct?
- 这个代码正确吗?它是否正确地确保了相互排斥
- 它能在所有x86操作系统上工作吗
- 它也适用于x86_64吗?在所有操作系统上
- 它是最优的吗?
- 我见过使用比较和交换的自旋锁实现,但我不确定哪个更好
- 根据GCC原子内置文档(),还有
。我不是记忆障碍方面的专家,所以我不确定是否可以用它来代替\uuuu同步\uu锁定\uu释放
\uuuu sync\u synchronize
- 我正在为没有争用的情况进行优化
我一点也不在乎争论。每隔几天可能会有1到2个其他线程尝试锁定自旋锁。回答您的问题:
unlock()
案例中使用\u sync\u lock\u release()
可能会稍微好一点;因为这将在一次操作中减少锁并添加内存屏障。然而,假设你的断言很少会有争论;我觉得不错我不能对正确性发表评论,但在我阅读问题正文之前,你问题的标题就已经引起了我的注意。同步原语很难保证正确性。。。如果可能的话,最好使用设计良好/维护良好的库,或者。一个改进建议是使用(test和test并设置)。使用CAS操作对于处理器来说是非常昂贵的,因此如果可能,最好避免使用CAS操作。
另一件事,确保您不会遭受优先级反转的影响(如果高优先级线程尝试获取锁,而低优先级线程尝试释放锁,该怎么办?例如,在Windows上,调度程序使用优先级提升最终会解决此问题,但如果在过去20次尝试中未成功获取锁,您可以显式放弃线程的时间片。)(例如…)在我看来很好。顺便说一句,这里的实现即使在有争议的情况下也更有效
void lock(volatile int *exclusion)
{
while (__sync_lock_test_and_set(exclusion, 1))
while (*exclusion)
;
}
您的解锁过程不需要内存屏障;只要在x86上dword对齐,对排除的分配就是原子的。如果您使用的是最新版本的Linux,则可以使用--a“快速用户空间互斥”: 正确编程的futex锁将不会使用系统调用,除非该锁处于争用状态 在无争议的情况下,您正试图使用自旋锁对其进行优化,futex将像自旋锁一样运行,而不需要内核系统调用。如果锁有争议,等待将在内核中进行,而不需要忙着等待。所以我想:
* Is it correct?
在上述情况下,我想说是的
* Is it optimal?
这是一个复杂的问题。通过重新发明轮子,你也重新发明了许多其他实现已经解决的问题
- 如果您不尝试访问锁字,则可能会在失败时出现浪费循环
- 在解锁中使用完整的屏障只需要具有释放语义(这就是为什么您要使用uu sync_lock_release,这样您就可以在安腾上获得st1.rel而不是mf,或者在powerpc上获得lwsync等)。如果您真的只关心x86或x86 64,那么这里使用的屏障类型与否并不重要(但如果你想在哪里使用英特尔安腾的HP-IPF端口,你就不会想要这个)
- 您没有通常放在废物循环之前的pause()指令
- 当有争论时,你想要一些东西,semop,甚至是绝望中的一个愚蠢的睡眠。如果你真的需要这为你带来的性能,那么futex建议可能是一个好建议。如果你需要这为你带来的性能差到足以维护这段代码,你有很多研究要做
在x86上,sync_lock_test_和_集将映射到一个带有隐含锁前缀的xchg指令。绝对是生成的最紧凑的代码(特别是如果您使用一个字节作为“锁字”而不是int),但其正确性不亚于使用lock CMPXCHG。比较和交换可以用于更高级的算法(比如在失败时将指向第一个“服务员”元数据的非零指针放入锁字中)。我想知道以下CAS实现是否是x86_64上的正确实现。 在我的i7 X920笔记本电脑(fedora 13 x86_64,gcc 4.4.5)上,速度几乎快了一倍
在x86(32/64)的特定情况下我认为在解锁代码中根本不需要内存隔离。x86不进行任何重新排序,只是存储首先放在存储缓冲区中,因此其他线程可以延迟它们的可见。如果一个线程进行存储,然后从同一个变量中读取,那么如果它尚未刷新到mem中,它将从其存储缓冲区中读取或
inline void lock(volatile int *locked) {
while (__sync_val_compare_and_swap(locked, 0, 1));
asm volatile("lfence" ::: "memory");
}
inline void unlock(volatile int *locked) {
*locked=0;
asm volatile("sfence" ::: "memory");
}