比如说,我有一个独立的基于原子操作的自旋锁实现,如下所示:
bool TryLock(volatile TInt32 * pFlag)
{
return !(AtomicOps::Exchange32(pFlag, 1) == 1);
}
void Lock (volatile TInt32 * pFlag)
{
while (AtomicOps::Exchange32(pFlag, 1) == 1) {
AtomicOps::ThreadYield();
}
}
void Unlock (volatile TInt32 * pFlag)
{
*pFlag = 0; // is this ok? or here as well a atomicity is needed for load and store
}
使用AtomicOps::Exchange32
在Windows上实现InterlockedExchange
,使用__atomic_exchange_n
在Linux上实现{。}}。
答案 0 :(得分:1)
在自旋锁实现中需要两个内存屏障:
TryLock()
和Lock()
中“获取障碍”或“导入障碍”。只有在pFlag
值更新后,它才会强制获取获取螺旋锁时发出的操作。Unlock()
中的“释放障碍”或“出口障碍”。它强制在释放螺旋锁之前发出的操作在更新pFlag
值之前可见。出于同样的原因,您还需要两个编译器障碍。
有关详细信息,请参阅this article。
这种方法适用于一般情况。在x86/64
:
提供了更多详细信息here。
以下是使用GCC atomic builtins的示例实现。它适用于GCC支持的所有架构:
代码:
bool TryLock(volatile bool* pFlag)
{
// acquire memory barrier and compiler barrier
return !__atomic_test_and_set(pFlag, __ATOMIC_ACQUIRE);
}
void Lock(volatile bool* pFlag)
{
for (;;) {
// acquire memory barrier and compiler barrier
if (!__atomic_test_and_set(pFlag, __ATOMIC_ACQUIRE)) {
return;
}
// relaxed waiting, usually no memory barriers (optional)
while (__atomic_load_n(pFlag, __ATOMIC_RELAXED)) {
CPU_RELAX();
}
}
}
void Unlock(volatile bool* pFlag)
{
// release memory barrier and compiler barrier
__atomic_clear(pFlag, __ATOMIC_RELEASE);
}
另见Linux kernel memory barriers作为一个很好的参考。
在您的实施中:
Lock()
调用AtomicOps::Exchange32()
已经包含编译器屏障并且可能获取或完全内存屏障(我们不知道因为您没有向__atomic_exchange_n()
提供实际参数)。Unlock()
错过了内存和编译器的障碍,因此它被破坏了。如果是选项,还可以考虑使用pthread_spin_lock()
。
答案 1 :(得分:1)
在大多数情况下,为了释放资源,只需将锁重置为零(就像你一样)几乎没问题(例如在英特尔酷睿处理器上),但你还需要确保编译器不会交换指令(参见下面,另见g-v的帖子)。如果你想要严谨(和便携),有两件事需要考虑:
编译器的作用:它可以交换优化代码的指令,因此如果它不是"意识到它会引入一些微妙的错误。代码的多线程性质。为避免这种情况,可以插入编译器屏障。
处理器的作用:某些处理器(如专业服务器中使用的Intel Itanium或智能手机中使用的ARM处理器)具有所谓的“放松内存模型”。 。实际上,这意味着处理器可能决定改变操作的顺序。同样,这可以通过使用特殊指令(加载障碍和存储障碍)来避免。例如,在ARM处理器中,指令DMB确保在下一条指令之前完成所有存储操作(并且需要将其插入到释放锁的函数中)
结论:如果您对这些功能有一些编译器/操作系统支持(例如,stdatomics.h
或C中的std::atomic
,那么使代码更正是非常棘手的++ 0x),依靠它们比编写自己更好(但有时你别无选择)。在标准英特尔酷睿处理器的特定情况下,如果您在发布操作中插入编译器屏障,我认为您所做的是正确的(参见g-v'帖子)。
关于编译时与运行时内存排序,请参阅:https://en.wikipedia.org/wiki/Memory_ordering
我在不同架构上实现的一些原子/自旋锁的代码: http://alice.loria.fr/software/geogram/doc/html/atomics_8h.html (但我不确定它是否100%正确)