这个简单(原子)锁是线程安全的吗?

7

这段代码是线程安全的吗?我需要在函数签名中加上volatile吗?(例如:void Unlock() volatile {v=0;})如果不是,我该如何使其线程安全?

class SimpleLock {
    std::atomic<int> v;
public:
    bool try_lock() { int z=0; return v.compare_exchange_strong(z, 1); }
    void lock() { while(try_lock()==false) std::this_thread::yield(); }
    void unlock() {v=0;}
};

请记住,这只是玩具代码,不应在任何真实应用程序中使用。与正确的锁相比,性能会非常糟糕。(仅举一个例子,想象一下如果没有其他线程可让出时间片,并且调用 lock 的线程正在与持有锁的线程在同一物理核心上运行。哎呀!) - David Schwartz
1个回答

8
是的,它是线程安全的,尽管你可以将 Lock 重命名为 TryLock,因为你没有在循环中调用 CAS 直到成功。传统上 Lock 操作被认为应该一直阻塞,直到获取成功。
关于 volatilestd::atomic 的文档 指定了(关于 = 操作符):

将值 t 原子地分配给原子变量。等同于 store(desired)。

然后是关于 store

void store( T desired, memory_order = std::memory_order_seq_cst );

然后是关于 memory_order = std::memory_order_seq_cst
  • 在写线程中,原子存储之后的任何写入都不能被重新排序
  • 在读线程中,原子加载之前的任何读取都不能被重新排序。
  • 在所有标记为 std::memory_order_seq_cst 的原子操作之间建立同步。使用这种原子操作的所有线程看到相同的内存访问顺序。
所以不需要在这里使用 volatile。此外,volatile 具有比上述保证更弱的保证(实际上,在 C++ 中 volatile 大多数都是无用的):

在执行线程中,对所有 volatile 对象的访问(读取和写入)保证不相对于彼此被重新排序,但是另一个线程可能无法观察到这个顺序,因为 volatile 访问不会建立线程间同步。


正确。你有没有想过它应该是 bool TryLock() volatile 还是不需要 volatile? - user34537
如果您想将此锁与标准库一起使用,最好将它们命名为bool try_lock()void unlock() - kennytm
2
回复:“volatile的保证比上述内容更弱。”确实:在标准C++中,它没有任何保证。它的用处取决于编译器文档所说的内容。 - Pete Becker
@PeteBecker:这有点夸张了,你是不是想加上“关于多线程”的后缀? - GManNickG
@GManNickG - 谢谢。我只是在考虑多线程。 :-( - Pete Becker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接