我最近在学习无锁并发中的可重入锁时遇到了以下代码:
class ReentrantLock32
{
std::atomic<std::size_t> m_atomic;
std::int32_t m_refCount;
public:
ReentrantLock32() : m_atomic(0), m_refCount(0) {}
void Acquire()
{
std::hash<std::thread::id> hasher;
std::size_t tid = hasher(std::this_thread::get_id());
if (m_atomic.load(std::memory_order_relaxed) != tid)
{
std::size_t unlockValue = 0;
while (!m_atomic.compare_exchange_weak(
unlockValue,
tid,
std::memory_order_relaxed,
std::memory_order_relaxed))
{
unlockValue = 0;
PAUSE();
}
}
++m_refCount;
std::atomic_thread_fence(std::memory_order_acquire);
}
void Release() {
std::atomic_thread_fence(std:memory_order_release);
std::hash<std::thread::id> hasher;
std::size_t tid = hasher(std::this_thread::get_id());
std::size_t actual = m_atomic.load(std::memory_order_relaxed);
assert(actual == tid);
--m_refCount;
if (m_refCount == 0)
{
m_atomic.store(0,std::memory_order_relaxed);
}
}
//...
}
但是,我不确定释放栅栏调用是否不排除在其之前的线程中进行后续内存操作的可能性,以及获取栅栏是否排除在其之后进行早期内存操作的可能性。如果他们不这样做,从技术上讲,优化是否可能会导致线路
if (m_refCount == 0)
在调用之前在同一线程上完整且成功地调用 Acquire() 即可成功
m_atomic.store(0,std::memory_order_relaxed);
在这种情况下,重新排序的 Acquire() 调用中的有效增量将被延迟的 store() 调用覆盖?
在分析这段代码时,我还发现可能存在过时的数据问题,导致重复的锁受到质疑here https://stackoverflow.com/questions/75078759/could-the-following-code-written-for-a-reentrant-lock-be-susceptible-to-a-stale.
还有另一个相关问题来澄清释放栅栏调用的内存操作的潜在顺序here https://stackoverflow.com/questions/75077371/when-exactly-does-a-release-fence-call-in-c-call-go-from-being-a-statement-to?noredirect=1#comment132490123_75077371.