2017-01-01 58 views
1

我知道我可以在一個類中使用一個mutex成員,並將其鎖定在每個方法中以防止多線程環境中的數據競爭。但是,如果在類的方法中存在嵌套調用,如下面的類中的add_one()add_two(),則此方法可能會導致死鎖。對每種方法使用不同的mutex是一種解決方法。但是,在嵌套調用的情況下,是否有更原則更優雅的方法來防止死鎖?設計線程安全類時避免嵌套調用造成死鎖

class AddClass { 
public: 
    AddClass& operator=(AddClass const&) = delete; // disable copy-assignment constructor 
    AddClass(int val) : base(val) {} 
    int add_one() { return ++base; } 
    int add_two() { 
    add_one; 
    add_one; 
    return base; 
    } 
private: 
    int base; 
}; 

回答

2

std::recursive_mutex正是爲了這個目的。

是避免開銷遞歸互斥體發生的另一種方法是從民辦非同步執行獨立的公同步接口:

class AddClass { 
public: 
    AddClass& operator=(AddClass const&) = delete; // disable copy-assignment constructor 
    AddClass(int val) : base(val) {} 
    int add_one() { 
    std::lock_guard<std::mutex> guard{mutex}; 
    return add_one_impl(); 
    } 
    int add_two() { 
    std::lock_guard<std::mutex> guard{mutex}; 
    return add_two_impl(); 
    } 
private: 
    int base; 
    std::mutex mutex; 
    int add_one_impl() { 
    return ++base; 
    } 
    int add_two_impl() { 
    add_one_impl(); 
    add_one_impl(); 
    return base; 
    } 
}; 

但是請注意,這是不可能的。例如,如果您有一個接受回調並在持有鎖的同時調用它的方法,則回調可能會嘗試調用您的類的其他公共方法,並且您將再次面臨雙重鎖定嘗試。

+2

鎖定和實現的關注分離是正確的方法。遞歸互斥應該是最後的手段。 +1 –

0

對此的一般的解決方案被稱爲reentrant mutex

雖然任何企圖在普通互斥 (鎖)執行「鎖定」操作將失敗或塊時互斥是已經鎖定, 遞歸互斥這個操作將會成功,當且僅當 鎖定線程是已經擁有鎖定的鎖定線程。典型地,遞歸互斥鎖跟蹤其被鎖定的次數,並且 在其他線程可能鎖定它之前需要執行同樣多的解鎖操作。

有一個在C++ 11標準庫:http://en.cppreference.com/w/cpp/thread/recursive_mutex

0

A 遞歸互斥鎖是一個可鎖定的對象,就像互斥體一樣,但允許相同的線程獲取互斥對象的多個所有權級別。

何時以及如何使用遞歸Mutex--以下鏈接

Recursive Mutex

注:遞歸和非遞歸互斥體有不同的使用情況。

希望它有幫助。

0

使用鎖定你的鎖和調用執行工作的私有成員函數的函數實現公共接口。私有函數可以互相調用,而無需重新鎖定互斥鎖的開銷,也不需要使用遞歸互斥鎖,這被許多人視爲設計失敗的標誌。