[英]why do we need a reference count in this Reentrant lock example?
為什么我們在下面的示例中需要m_refCount
? 如果我們將它排除在外並刪除 if 語句並將其主體留在那里會發生什么?
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 this thread doesn't already hold the lock...
if (m_atomic.load(std::memory_order_relaxed) != tid)
{
// ... spin wait until we do hold it
std::size_t unlockValue = 0;
while (!m_atomic.compare_exchange_weak(
unlockValue,
tid,
std::memory_order_relaxed, // fence below!
std::memory_order_relaxed))
{
unlockValue = 0;
PAUSE();
}
}
// increment reference count so we can verify that
// Acquire() and Release() are called in pairs
++m_refCount;
// use an acquire fence to ensure all subsequent
// reads by this thread will be valid
std::atomic_thread_fence(std::memory_order_acquire);
}
void Release()
{
// use release semantics to ensure that all prior
// writes have been fully committed before we unlock
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)
{
// release lock, which is safe because we own it
m_atomic.store(0, std::memory_order_relaxed);
}
}
bool TryAcquire()
{
std::hash<std::thread::id> hasher;
std::size_t tid = hasher(std::this_thread::get_id());
bool acquired = false;
if (m_atomic.load(std::memory_order_relaxed) == tid)
{
acquired = true;
}
else
{
std::size_t unlockValue = 0;
acquired = m_atomic.compare_exchange_strong(
unlockValue,
tid,
std::memory_order_relaxed, // fence below!
std::memory_order_relaxed);
}
if (acquired)
{
++m_refCount;
std::atomic_thread_fence(
std::memory_order_acquire);
}
return acquired;
}
};
編輯:示例來自 Jason Gregory 的一本名為“游戲引擎架構第三版”的書
需要計數來實現遞歸鎖定。 如果它不存在,無論有多少Acquire
調用, Release
總是會解鎖,這在許多情況下不是您所期望和想要的。
考慮以下常見模式:
void helper_method(){
Acquire();
// Work #2
Release();
}
void method(){
Acquire();
// Work #1
helper_method();
// Work #3
Release();
}
如果鎖不是遞歸的,則必須小心。 在這種情況下, #3
不再被稱為 lock 並且您現在有一個難以追蹤的錯誤。 它的發生只是因為helper_method
中的Release()
解鎖了鎖,這樣做是出於善意,因為它首先鎖定了它,而不知道它之前已經被鎖定了。 這也是為什么有std::mutex
和std::recursive_mutex
的原因,鎖定前者兩次是UB(根據我的經驗經常會死鎖)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.