2011-03-17 21 views
13

Threads and simple Dead lock cure ve ayrıca Herb Sutter adresinde bulunan yanıtlara göre kilitlenmeden kaçınmanın anahtarı kilit hiyerarşileri kullanmaktır.Kilit hiyerarşisi oluşturma araçları

Bunun için destek sağlayan iyi C++ kütüphaneleri var mı? Boost veya Poco'da bulamıyorum.

İdeal olarak, hiyerarşiyi derleme zamanında tanımlayan bir sistem olurdu. Belki de bu şekilde görünecektir:

template<class LowerLevelMutex> 
class RankedMutex { ... }; 

class BottomMutex { ... }; 

typedef RankedMutex<BottomMutex> L1Mutex; 
typedef RankedMutex<L1Mutex> L2Mutex; 
typedef RankedMutex<L2Mutex> L3Mutex; 
// ... 
+2

benim cevap belirttiğimiz gibi kilitlenme kaçınarak kimse anahtar ve ettik basit bandaj yoktur anladım. Tasarım sürecinde bunu önlemek için çok daha iyi. Özel durumun nedir? –

+0

@Platinum Azure: Eski ve büyük bir kod tabanında kilitlenme sorunları için çözümler arıyorum. – StackedCrooked

+1

O zaman hiçbir şeyim yok üzgünüm. :-( –

cevap

6

Evet, kilit hiyerarşileri kilitlenmeyi etkili bir şekilde önleyebilir; Elbette programınız için bir hiyerarşi tanımlayabiliyor olsanız da (özellikle eklentilerin varlığında) tamamen başka bir konu.

temel bloklar basittir:

  • Her muteks bir seviyeye sahip olmalıdır (ya derleme zamanında belirlenen veya çalışma zamanı)
  • sadece hiç artan Muteksleri veya azalan düzeyi (edinmeniz gerekmektedir Her iş parçacığı bir kere karar verin)

Umarım adalet fikrini uygulayabilirim, lütfen örnek çizimi bir çizimin altında düşünün; hiçbir zaman derlenmiş/test edilmemiştir.

Temel muteksin:

template <typename Mutex, size_t Level> 
class HierarchicalMutex { 
public: 
    friend class LevelManager; 

    void lock() { 
     LevelManager::Lock(*this); 
    } 

    void unlock() { 
     LevelManager::Unlock(*this); 
    } 

private: 
    size_t previous; 
    Mutex mutex; 
}; // class HierarchicalMutex 

template <typename Mutex, size_t Level> 
size_t level(HierarchicalMutex<Mutex,Level> const&) { return Level; } 

LevelManager rolü olduğu seviye geçişleri doğru sırayla gerçekleşmesi sağlamak için basitçe. tekmeler için

class LevelManager { 
public: 
    // 
    // Single Mutex locking 
    // 
    template <typename M> 
    static void Lock(M& m) { 
     m.previous = LevelUp(level(m)); 
     m.mutex.lock(); 
    } 

    template <typename M> 
    static void Unlock(M& m) { 
     m.mutex.unlock(); 
     LevelDown(level(m), m.previous); 
    } 

    // 
    // Multiple Mutexes Group Locking 
    // 
    // Note: those should expose a "size_t level(M const&)" function, 
    //  and calls to lock/unlock should appropriately call 
    //  this manager to raise/lower the current level. 
    // 
    // Note: mutexes acquired as a group 
    //  should be released with the same group. 
    // 
    template <typename M> 
    static void Lock(std::array_ref<M*> mutexes) { // I wish this type existed 
     using std::begin; using std::end; 

     auto begin = begin(mutexes); 
     auto end = end(mutexes); 

     end = std::remove_if(begin, end, [](M const* m) { return m == 0; }); 

     if (begin == end) { return; } 

     Sort(begin, end); 

     size_t const previous = LevelUp(level(*std::prev(end))); 

     for (; begin != end; ++begin) { 
      begin->previous = previous; 
      begin->mutex.lock(); 
     } 
    } 

    template <typename M> 
    static void Unlock(std::array_ref<M*> mutexes) { 
     using std::begin; using std::end; 

     auto begin = begin(mutexes); 
     auto end = end(mutexes); 

     end = std::remove_if(begin, end, [](M const* m) { return m == 0; }); 

     if (begin == end) { return; } 

     Sort(begin, end); 

     std::reverse(begin, end); 

     for (auto it = begin; it != end; ++it) { it->mutex.unlock(); } 

     LevelDown(level(*begin), begin->previous); 
    } 

private: 
    static __thread size_t CurrentLevel = 0; 

    template <typename It> 
    static void Sort(It begin, It end) { 
     using Ref = typename std::iterator_traits<It>::const_reference; 

     auto const sorter = [](Ref left, Ref right) { 
      return std::tie(level(left), left) < std::tie(level(right), right); 
     }; 

     std::sort(begin, end, sorter); 
    } 

    static size_t LevelUp(size_t const to) { 
     if (CurrentLevel >= to) { throw LockHierarchyViolation(); } 
     CurrentLevel = to; 
    } 

    static void LevelDown(size_t const from, size_t const to) { 
     if (CurrentLevel != from) { throw LockHierarchyViolation(); } 
     CurrentLevel = to; 
    } 
}; // class LevelManager 

, tek bir çekimde aynı seviyede katları kilitleri kilitlemek imkanı uyguladı.

+0

Bu gerçekten harika görünüyor. Cevap için teşekkürler. Tutarsız kilitleme düzenlerini algılayan bir [çalışma zamanı denetleyicisi] (https://stacked-crooked.googlecode.com/svn/trunk/Playground/LockOrderChecker/main.cpp) ile çalıştım, ancak bunu kullanmam gerekiyordu. Sıfırdan yeniden yazma iznim olan eski bir projeydi. Kilitsiz bir yaklaşım için seçtim. Btw, ben bu konuda yazdığınız bu şifreli deyimi icat ettim. [İplik erişim kontrolü için deney yapıyorum.] (Http://coliru.stacked-crooked.com/a/6bbeb77f54234db6) – StackedCrooked

1

Eğer durum bu tür yapabileceğiniz en önemli şey sadece kilitleri daima hiyerarşik (iç içe anlamında) uygulandığından emin olun edilir. Bu şekilde, seviye 2 kilidine sahip olmadan seviye 3 kilidine erişemezsiniz, böylece seviye 1 kilidine sahip olmadan erişemediğiniz seviye. İlk önce 1 ve 2'ye ulaşmadan 3'e bile çıkamayacaksınız, bu yüzden büyük problemleri önlemelisiniz.

Ortaya çıkan kilitlenme durumlarının bazılarında daha spesifik olabilir misiniz? Belki de, yukarıda anlattığım gibi manipüle edilemeyecek kadar karmaşık olan bazı karmaşık şeyler için bir çözüm bulabiliriz.

4

Hiyerarşiyi yönetmek için ayrı bir sınıfa gerek yoktur. Güzel bir çözüm Anthony Williams (ISBN 9781933988771) tarafından, Eylem içinde C++ eşzamanlılık bulunabilir:

#include <mutex> 
#include <stdexcept> 

class hierarchical_mutex 
{ 
    std::mutex internal_mutex; 
    unsigned long const hierarchy_value; 
    unsigned long previous_hierarchy_value; 
    static thread_local unsigned long this_thread_hierarchy_value; 

    void check_for_hierarchy_violation() 
    { 
     if(this_thread_hierarchy_value <= hierarchy_value) 
     { 
      throw std::logic_error("mutex hierarchy violated"); 
     } 
    } 
    void update_hierarchy_value() 
    { 
     previous_hierarchy_value=this_thread_hierarchy_value; 
     this_thread_hierarchy_value=hierarchy_value; 
    } 
public: 
    explicit hierarchical_mutex(unsigned long value): 
     hierarchy_value(value), 
     previous_hierarchy_value(0) 
    {} 
    void lock() 
    { 
     check_for_hierarchy_violation(); 
     internal_mutex.lock(); 
     update_hierarchy_value(); 
    } 
    void unlock() 
    { 
     this_thread_hierarchy_value=previous_hierarchy_value; 
     internal_mutex.unlock(); 
    } 
    bool try_lock() 
    { 
     check_for_hierarchy_violation(); 
     if(!internal_mutex.try_lock()) 
      return false; 
     update_hierarchy_value(); 
     return true; 
    } 
}; 
thread_local unsigned long 
    hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);  

int main() 
{ 
    hierarchical_mutex m1(42); 
    hierarchical_mutex m2(2000); 

} 
İlgili konular