2016-03-14 52 views
0

foo類有一個方法bar。根據某些同步協議,特定foo對象的bar方法將在任何時間點僅由最多一個線程調用。一種高效非強制實施,檢驗互斥

我想補充一個非常輕巧verification_mutex驗證這一/調試同步濫用。類似地,將用於常規的互斥體:但是

class foo { 
public: 
    void bar() { 
     std::lock_guard<verification_mutex> lk{m}; 
     ... 
    } 

private: 
    mutable verification_mutex m; 
}; 

,它本身不會必然鎖定或解鎖任何東西。如果檢測到多線程同時訪問,它將只是throw。重點是儘可能降低其運行時間佔用(包括對其他代碼的影響,例如通過memory barriers)。

這裏是實現verification_mutex三個選項:

  1. 的包裝紙圍繞std::mutex,但通過檢查trylock成功實現lock(這是剛拿到的想法;顯然不是非常快)
  2. 一個原子變量,記錄了當前的「鎖定」線程ID,原子操作爲exchange(請參見下面的實現草圖)。
  3. 與2相同,但沒有原子。

這些是正確或不正確(尤其是2和ESP 3)?他們將如何影響性能(特別是周圍的代碼)?有沒有一個完全優越的選擇?下面


編輯通過@SergeyA答案是好的,但我特別好奇的記憶障礙。一個不利用它們的解決方案將會很好,如果答案給出一些直觀的解釋,爲什麼任何解決方案都會失敗。


實現素描

#include <atomic>                                                
#include <thread> 
#include <functional> 

class verification_mutex { 
public: 
    verification_mutex() : m_holder{0}{} 

    void lock() { 
     if(m_holder.exchange(get_this_thread_id()) != 0) 
      throw std::logic_error("lock"); 
    } 

    void unlock() { 
     if(m_holder.exchange(0) != get_this_thread_id()) 
      throw std::logic_error("unlock"); 
    } 

    bool try_lock() { 
     lock(); 
     return true; 
    } 

private: 
    static inline std::size_t get_this_thread_id() { 
     return std::hash<std::thread::id>()(std::this_thread::get_id()); 
    } 

private: 
    std::atomic_size_t m_holder; 
}; 
+0

的的tryLock具有實際持有鎖,否則你無法檢測,如果其他線程是「在那裏」。所以它不會比鎖更快。你需要知道這個錯誤有多強烈?可靠的,還是這只是測試? – Yakk

+0

你爲什麼想要重入鎖? – SergeyA

+0

此外,請注意添加任何類型的同步將你的減緩由於內存屏障效應(以及同步無記憶障礙是無用的) – SergeyA

回答

1

選項3是不可行的。從多個線程讀取/寫入變量時,您需要內存屏障。

所有選項中,原子布爾變量將是最快的,因爲它不會需要上下文切換(互斥可能)。類似的東西:

class verifying_mutex { 
    std::atomic<bool> locked{false}; 
public: 
    bool lock() { 
     if (!locked.compare_exchange_strong(false, true)) 
     throw std::runtime_error("Incorrect mt-access pattern"); 
    } 

    bool unlock() { 
     locked = false; 
    } 
}; 

在一個側面說明,你原來的鎖版本中使用的thread_id,這會減慢你的速度是不必要的。不要這樣做。

+0

關於使用thread_id的替代方法的好處。但是,這是一個折衷,因爲線程ID可以用於某些診斷。 –