2016-12-16 107 views
7

boost::shared_mutexstd::shared_mutex(C++ 17)可以用於單個寫入器,多個讀取器訪問。作爲一個教育練習,我將一個使用spinlocking的簡單實現放在一起,並具有其他限制(例如公平性策略),但顯然不打算用於實際應用。C++ shared_mutex實現

這個想法是,如果沒有線程持有鎖,互斥量會保持爲零的引用計數。如果> 0,則該值表示有權訪問的讀取器的數量。如果-1,一個作家有權訪問。

這是一個沒有數據競爭的正確實現(尤其是使用了最小的內存排序)嗎?

#include <atomic> 

class my_shared_mutex { 
    std::atomic<int> refcount{0}; 
public: 

    void lock() // write lock 
    { 
     int val; 
     do { 
      val = 0; // Can only take a write lock when refcount == 0 

     } while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire)); 
     // can memory_order_relaxed be used if only a single thread takes write locks ? 
    } 

    void unlock() // write unlock 
    { 
     refcount.store(0, std::memory_order_release); 
    } 

    void lock_shared() // read lock 
    { 
     int val; 
     do { 
      do { 
       val = refcount.load(std::memory_order_relaxed); 

      } while (val == -1); // spinning until the write lock is released 

     } while (!refcount.compare_exchange_weak(val, val+1, std::memory_order_acquire)); 
    } 

    void unlock_shared() // read unlock 
    { 
     refcount.fetch_sub(1, std::memory_order_relaxed); 
    } 
}; 

回答

3

(我使用CMPXCHG作爲簡寫C++ compare_exchange_weak功能,而不是cmpxchg instruction)。

lock_shared絕對看起來不錯:在讀取時旋轉並且只在嘗試cmpxchg時性能好於在cmpxchg上旋轉時的性能。儘管我認爲你是爲了正確而被迫進入的,爲了避免將-1改爲0並解鎖寫鎖。

我覺得unlock_shared應該使用mo_release,不mo_relaxed,因爲它需要從共享數據結構責令負荷,以確保一個作家不啓動從閱讀器的關鍵部分的負荷發生前寫作。 (LoadStore reordering是弱結構體系結構中的一件事,儘管x86只對StoreLoad進行重新排序。)A Release operation will order preceding loads and keep them inside the critical section


(在寫lock)://可以memory_order_relaxed被如果只有一個線程需要寫鎖使用?

沒有,你仍然需要保持臨界區裏面寫的,所以CMPXCHG仍需要同步隨着(在C++術語)釋放 - 存儲從unlock_shared

+0

我對unlock_shared中的內存順序並不確定,但我的推理是它並不真正「釋放」任何東西,因爲它具有隻讀訪問權限,無法更改它所保護的數據 – LWimsey

+0

@LWimsey:是的,它比較難考慮加載排序比商店排序,但它是一個真實的事情。當它從L1緩存中讀取數據時,負載變得全局可見。 (因爲這是從全局一致的緩存複製到單個CPU的無序內核的副本。) –