簡體   English   中英

為什么在這個可重入鎖示例中需要引用計數?

[英]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::mutexstd::recursive_mutex的原因,鎖定前者兩次是UB(根據我的經驗經常會死鎖)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM