2012-11-30 79 views
4

我想使用遞歸QMutex,我閱讀QMutex類參考,但我不明白如何做到這一點,有人可以給我一個例子嗎? 我需要某種方式來鎖定可以在調用鎖定方法之前或之後解鎖的QMutex。 如果遞歸互斥是不是有什麼其他方式?如何使用遞歸QMutex

+0

您能再詳細解釋一下您的需求嗎?你的意思是你需要在鎖定方法之前或之後解鎖嗎?你的意思是在方法結束後自動解鎖? – jdi

回答

7

要創建一個遞歸QMutex您只需通過QMutex::Recursive在施工時間,例如:

QMutex mutex(QMutex::Recursive); 
int number = 6; 

void method1() 
{ 
    mutex.lock(); 
    number *= 5; 
    mutex.unlock(); 
} 

void method2() 
{ 
    mutex.lock(); 
    number *= 3; 
    mutex.unlock(); 
} 

Recursive意味着你可以鎖定多次在同一個線程的互斥體,你不必解鎖。如果我理解得很清楚你的問題就是你想要的。

要小心,如果您遞歸鎖定,則必須調用解鎖次數相同的次數。一種更好的方式,以鎖定/解鎖互斥使用QMutexLocker

#include <QMutexLocker> 

QMutex mutex(QMutex::Recursive); 
int number = 6; 

void method1() 
{ 
    QMutexLocker locker(&mutex); // Here mutex is locked 
    number *= 5; 
    // Here locker goes out of scope. 
    // When locker is destroyed automatically unlocks mutex 
} 

void method2() 
{ 
    QMutexLocker locker(&mutex); 
    number *= 3; 
} 
2

遞歸模式僅僅意味着如果一個線程擁有一個互斥體,並且同一個線程試圖再次鎖定互斥體,那麼它將成功。要求是對lock/unlock的呼叫是平衡的。

在非遞歸模式下,這將導致死鎖。

3

遞歸互斥可以從一個單獨的線程多次鎖定而不需要解除鎖定,只要相同數量的解鎖呼叫是從由同一個線程。當共享資源被多於一個函數使用時,這種機制就派上用場了,其中一個函數調用另一個使用資源的函數。

考慮下面的類:

class Foo { 
    public: 
    Foo(); 
    void bar(); // Does something to the resource 
    void thud(); // Calls bar() then does something else to the resource 
    private: 
    Resource mRes; 
    QMutex mLock; 
} 

最初的實現可能像下面這樣:

Foo::Foo() {} 

void Foo::bar() { 
    QMutexLocker locker(&mLock); 
    mRes.doSomething(); 
} 

void Foo::thud() { 
    QMutexLocker locker(&mLock); 
    bar(); 
    mRes.doSomethingElse(); 
} 

上面的代碼將僵局調用轟的一聲。 mLock將在thud()的第一行中被獲取,並再次被bar()中的第一行獲得,這將阻止等待thud()釋放鎖定。

一個簡單的解決方案是鎖定ctor中的遞歸。

Foo::Foo() : mLock(QMutex::Recursive) {} 

一個OK的修復,並適用於許多情況,但應該意識到可能存在的性能損失使用,因爲每次遞歸互斥調用此解決方案可能需要一個系統調用來識別當前線程ID。

除了線程ID檢查,所有對thud()的調用仍然執行兩次QMutex :: lock()!

需要遞歸的設計可能會被重構以消除對遞歸互斥體的需要。一般來說,對遞歸互斥的需求是一種「代碼異味」,並且表明需要遵守分離關注原則。

對於Foo類,可以想象創建一個私有函數調用,它執行共享計算並在公共接口級別保持資源鎖定。

class Foo { 
    public: 
    Foo(); 
    void bar(); // Does something to the resource 
    void thud(); // Does something then does something else to the resource 
    private: 
    void doSomething(); 
    private: 
    Resource mRes; 
    QMutex mLock; 
} 

Foo::Foo() {} 

// public 
void Foo::bar() { 
    QMutexLocker locker(&mLock); 
    doSomething(); 
} 

void Foo::thud() { 
    QMutexLocker locker(&mLock); 
    doSomething(); 
    mRes.doSomethingElse(); 
} 

// private 
void Foo::doSomething() { 
    mRes.doSomething();  // Notice - no mutex in private function 
}