linux - 基于原子操作的自旋锁的Unlock可以直接将锁标志设置为零吗?

标签 linux multithreading atomic memory-barriers spinlock

例如,我有一个基于原子操作的专有自旋锁实现,如下所示:

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.hstd::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/

相关文章:

linux - 如何在 Linux 上的当前 shell 中执行脚本?

linux库问题

c++ - std::atomic 用于内置类型 - 非无锁与琐碎的析构函数?

c++ - 在发布序列中使用原子读-修改-写操作

c++11 - g++ 4.4.3 中的 std::atomic 支持

c - 使用我自建库的错误

在内存中创建和使用新堆栈

python - 多线程迭代器

c++ - 同类型的STL容器类线程安全吗?

使用 boost::asio::thread_pool 的 C++ 线程池,为什么我不能重用我的线程?