2017-06-15 44 views
1

我正試圖解決C++ 11中的生產者消費者問題。 我有一個保存資源的對象,多個線程可以添加或使用這些資源。我的問題是,當我嘗試在該對象上實現 a「當可用時使用」方法。 請假設插入/刪除操作的複雜性很小。如何在C++中正確解決生產者消費者問題11

對代碼邏輯的一點說明。

struct ResourceManager{ 
    std::mutex mux; 
    std::unique_lock lock{mux}; 
    std::condition_variable bell; 

    void addResource(/*some Resource*/){ 
    lock.lock(); 
    //add resource 
    lock.unlock(); 
    bell.notify_one(); //notifies waiting consumer threads to consume 
    } 

    T getResource(){ 
    while(true){ 
     lock.lock(); 
     if(/*resource is available*/){ 
     //remove resource from the object 
     lock.unlock(); 
     return resource; 
     }else{ 
     //new unique lock mutex object wmux creation 
     lock.unlock(); //problem line 
     bell.wait(wmux); //waits until addResource rings the bell 
     continue; 
     } 
    } 
    } 

}; 

假設以下情形:
- 兩個線程,T1,T2,幾乎同時調用addResource,的getResource。

-T2鎖定互斥鎖,並發現沒有更多可用資源,
因此它必須阻塞,直到有新資源可用。
因此它解鎖互斥鎖並設置等待的鐘。

-T1運行速度更快。當互斥鎖被解鎖時,
它立即添加資源,並且在T2設置等待響鈴之前,
T1已經響了,不通知任何人。

-T2無限期地等待鈴響,但沒有進一步的資源被添加。

我假設一個線程鎖定互斥鎖,可能是唯一一個解鎖它的 。因此,如果我在解鎖互斥鎖之前嘗試調用bell.wait,則互斥鎖永遠無法解鎖。

如果可能,我想不使用時間等待或多次檢查解決方案。
那麼我可以在C++ 11中解決這個問題?

+0

可能與您的問題無關,但請使用'std :: unique_lock'等鎖定鎖來鎖定/解鎖互斥鎖。 –

+0

鎖是一個unique_lock –

+0

什麼是'wmux'?你應該鎖定'lock',並執行'bell.wait(lock)',以避免競爭狀態。 – erenon

回答

1
lock.unlock(); //problem line 
    bell.wait(wmux); //waits until addResource rings the bell 

是的,這確實是問題線。

要正確使用作爲設計條件變量,你解鎖wait()之前互斥荷蘭國際集團在其相關的條件變量。 wait()在一個條件變量上,在等待期間原子地解鎖它,並且一旦線程已經被執行,重新獲得互斥鎖。解鎖和等待以及被通知並鎖定後的喚醒都是原子操作。

所有notify() s應該在鎖定互斥鎖時發出。所有wait()也是在互斥鎖完全鎖定的情況下完成的。正如我所提到的那樣,notify()是原子的,這導致所有與互斥體相關的操作都是原子性的和完全排序的,包括管理受互斥體保護的資源,以及通過條件變量進行線程通知,現在也受互斥體保護。

有一些設計模式可以在不使用互斥保護的情況下通知條件變量。但是它們更難以正確實現並且仍然實現線程安全語義。所有的條件變量操作也受互斥鎖保護,除了互斥鎖保護的所有其他功能外,實現起來也更加簡單。

+0

謝謝你的回答。 因此,如果我理解正確,在addResource中,應該在解鎖互斥體之前調用bell.notify_one()。 另外,在getResource中,我應該刪除lock.unlock(),並使用bell.wait(lock)而不是bell.wait(wmux); 我說得對嗎? –

+0

這聽起來正確;當然這取決於實際的實現細節,因爲你只是在你的問題中總結了你的代碼。注意[wait()]的要求(http://en.cppreference.com/w/cpp/thread/condition_variable/wait):「lock - std :: unique_lock 類型的對象,其中***必須是被當前線程鎖定***「。 –

1

std::condition_variable::wait需要在您的互斥鎖上傳遞鎖定的std::unique_lockwait將作爲其操作的一部分解鎖互斥鎖,並在它返回之前重新鎖定它。

使用類似std::lock_guardstd::unique_lock的鎖保護裝置的正常方式是在本地構建它們,並讓它們的構造函數鎖定您的互斥鎖,並將它們的析構函數解鎖。

此外,您可以通過提供std::condition_variable::wait的謂詞來避免原始代碼中的外部while循環。

struct ResourceManager { 
    std::mutex mux; 
    std::condition_variable bell; 

    void addResource(T resource) 
    { 
    std::lock_guard<std::mutex> lock{mux}; 
    // Add the resource 
    bell.notify_one(); 
    } 

    T getResource() 
    { 
    std::unique_lock<std::mutex> lock{mux}; 
    bell.wait(lock, [this](){ return resourceIsAvailable(); }); 
    return // the ressource 
    } 
}; 
+0

謝謝你的時間,你的回答很好地描述了另一個人以前已經回答過的內容。可惜他刪除了他的帖子:S –