例如,我有一个基于原子操作的专有自旋锁实现,如下所示:
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
实现,在 Linux 上使用 __atomic_exchange_n
实现。
最佳答案
在大多数情况下,为了释放资源,只需将锁定重置为零(就像您所做的那样)就几乎可以了(例如在英特尔酷睿处理器上),但您还需要确保编译器不会交换指令(请参阅下面,另请参阅 g-v 的帖子)。如果您想要严谨(并且可移植),则需要考虑两件事:
编译器的作用:它可能会交换优化代码的指令,如果它不“意识到”代码的多线程性质,就会引入一些微妙的错误。为了避免这种情况,可以插入编译器屏障。
处理器的作用:某些处理器(例如专业服务器中使用的 Intel Itanium 或智能手机中使用的 ARM 处理器)具有所谓的“宽松内存模型”。实际上,这意味着处理器可以决定改变操作的顺序。同样,可以通过使用特殊指令(加载屏障和存储屏障)来避免这种情况。例如,在ARM处理器中,指令DMB确保所有存储操作在下一条指令之前完成(并且需要将其插入到释放锁的函数中)
结论:如果您有一些对这些功能的编译器/操作系统支持(例如 stdatomics.h
或 std::atomic
in C++0x),依赖它们比自己编写要好得多(但有时你别无选择)。在标准英特尔酷睿处理器的具体情况下,我认为你所做的是正确的,只要你在发布操作中插入编译器屏障(参见 g-v 的帖子)。
关于编译时与运行时内存排序,请参阅:https://en.wikipedia.org/wiki/Memory_ordering
我在不同架构上实现的一些原子/自旋锁的代码: http://alice.loria.fr/software/geogram/doc/html/atomics_8h.html (但我不确定它是否 100% 正确)
关于linux - 基于原子操作的自旋锁的Unlock可以直接将锁标志设置为零吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32658024/