2010-10-13 58 views
7

我注意到boost並沒有支持信號量。達到類似效果的最簡單方法是什麼?如何在C++中使用boost來實現類似於信號量的功能?

+0

您能否更具體地瞭解您要查找的行爲?鑑於人們已經提出了大約14種不同類型的信號量。 – jalf 2010-10-14 00:08:42

+0

現在,例如,通過將用餐人數限制在4以下來解決用餐哲學家的問題(通過5位哲學家),通過信號量,我可以將初始值設爲4,哲學家等待信號量並完成信號。 – jonderry 2010-10-14 00:29:27

回答

6

您需要Boost Interprocess semaphoreBoost Thread synchronization原語。

Mutex/Lockcondition是通常用於同步跨單個進程的多個線程訪問共享資源的原語。有exclusive,readers-writerrecursive/reentrant類型的互斥。換句話說,互斥鎖是獨佔鎖。當需要解鎖互斥體並等待對象改變時,條件用於實現原子性。當你開始等待一個條件時,它解鎖互斥並保證不會解鎖+等待調用是原子的,其他線程無法修改這兩個操作之間的資源。

信號量在另一種情況下是條件和互斥量的混合,並且用於完全相同的目的,但同步跨進程的訪問。

請參閱Mutex vs Semaphore

也有這樣的事情,如non-blocking/lock-free synchronization這些日子變得非常流行。我個人在高頻交易應用中使用它,因爲數據量相對較大,低延遲確實很重要。

在你的情況下,我假設有5個哲學家可以在5個線程的單個進程中進行晚餐。在這種情況下,你必須使用互斥量,而不是信號量。你可能會也可能不會使用條件。這取決於你想要實現該餐飲程序的確切程度和準確程度。

我不知道如何更好地描述它,因爲我最終會寫一本關於它的書。所以我建議你找一些已經寫好的書來理解基本概念。一旦您瞭解了基本知識,您就可以使用像POSIX threads,Boost InterprocessThread,ACE或甚至non-blocking algorithms這樣的API /庫/框架來實現您想要的功能。

祝你好運!

+0

所以,出於好奇,「進程間信號量」這個名稱表明它意味着在進程之間而不是線程之間共享。這是否意味着它會在理論上使用一個introprocess信號量的前提下花費額外的開銷?我不確定如何輕鬆地將線程條件用於應用程序,例如我對該問題的評論中提到的玩具應用程序。 – jonderry 2010-10-14 00:51:21

+0

@jonderry:好的,我想我可以用一個簡單的答案逃脫,但你得到了我。我已經開始在評論中回覆,但由於鏈接太多,所以我最終編輯了我的答案。請參閱更新的版本。謝謝。 – 2010-10-14 02:14:57

+0

謝謝,弗拉德。誠然,餐飲哲學家的問題在相鄰的叉子上使用互斥體,但如果不添加更多的東西,就會陷入僵局。解決這個問題的一個標準方法是隻允許4位哲學家同時用餐,這樣人們總是可以取得進步。實現這一目標的自然方法是使用信號量。 – jonderry 2010-10-14 03:02:39

21

這是一種使用Boost.Thread實現非常簡單的信號量的方法。這是一個線程間信號量,而不是一個進程間信號量。沒有暗示的保證等 - 我甚至沒有編寫代碼。它演示了互斥鎖和條件變量是如何相互作用的,並且假設了一個合理的Boost最近版本。

注意互斥鎖和條件變量是如何「配對」的 - 線程必須鎖定互斥鎖以等待條件變量,並在被喚醒時重新獲取鎖。此外,更改數據的代碼需要明確地喚醒可能正在等待的其他代碼。這意味着互斥量,條件變量,數據和引起喚醒的條件都緊密耦合。緊密耦合還意味着數據,互斥鎖和條件變量應儘可能封裝 - 任何外部修改都可能以奇怪的方式破壞代碼,包括死鎖,錯過的喚醒和其他奇怪的錯誤。

所有這些實際上意味着作爲對Vlad Lazarenko答案的補充 - 理解理論和原則至少與在多線程編程中具有「工作」代碼一樣重要。

#include <boost/thread/condition_variable.hpp> 
#include <boost/thread/mutex.hpp>  
#include <boost/thread/lock_types.hpp> 


class semaphore 
{ 
    //The current semaphore count. 
    unsigned int count_; 

    //mutex_ protects count_. 
    //Any code that reads or writes the count_ data must hold a lock on 
    //the mutex. 
    boost::mutex mutex_; 

    //Code that increments count_ must notify the condition variable. 
    boost::condition_variable condition_; 

public: 
    explicit semaphore(unsigned int initial_count) 
     : count_(initial_count), 
     mutex_(), 
     condition_() 
    { 
    } 

    unsigned int get_count() //for debugging/testing only 
    { 
     //The "lock" object locks the mutex when it's constructed, 
     //and unlocks it when it's destroyed. 
     boost::unique_lock<boost::mutex> lock(mutex_); 
     return count_; 
    } 

    void signal() //called "release" in Java 
    { 
     boost::unique_lock<boost::mutex> lock(mutex_); 

     ++count_; 

     //Wake up any waiting threads. 
     //Always do this, even if count_ wasn't 0 on entry. 
     //Otherwise, we might not wake up enough waiting threads if we 
     //get a number of signal() calls in a row. 
     condition_.notify_one(); 
    } 

    void wait() //called "acquire" in Java 
    { 
     boost::unique_lock<boost::mutex> lock(mutex_); 
     while (count_ == 0) 
     { 
      condition_.wait(lock); 
     } 
     --count_; 
    } 

}; 
+0

工程就像一個魅力。太糟糕了,這不再是Boost本身的一部分。代碼爲 – Thomas 2011-10-01 14:45:04

+0

+ 1。 get count中的互斥體是否有任何意義?無論如何收到的人數都是「老」,不是嗎? – daramarak 2011-11-24 20:59:58

+1

正確,返回時計數會很舊。除了調試之外,獲取信號量的計數可能是程序中的一個錯誤。 – 2012-09-16 01:11:37

0

我做了一個信號燈類提升TimedLockable概念相兼容,所以它可以與像boost::unique_lock<semaphore>鎖使用。它不是一個古典定義中的信號量,但可以作爲一個使用。不過,希望對某人有用。

它以某種方式測試,但有很大的可能性,我做錯了什麼。如果有人能證明它的正確性,那將會很棒。

class semaphore 
{ 
private: 
    semaphore(const semaphore & other); 
    semaphore & operator = (const semaphore & other); 

    boost::mutex _mutex; 
    boost::condition_variable _condVar; 
    size_t _count; 

    class wait_predicate 
    { 
    private: 
     const size_t & _countRef; 
    public: 
     wait_predicate(const size_t & countRef) : _countRef(countRef) {} 
     bool operator()() { return _countRef > 0; } 
    }; 

    // must be used inside a locked scope! 
    inline wait_predicate getWaitPredicate() const 
    { 
     return wait_predicate(_count); 
    } 

public: 
    semaphore(size_t size): _count(size) 
    {} 

    void lock() 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     _condVar.wait(local_lock, getWaitPredicate()); 
     _count--; 
    } 

    void unlock() 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     _count++; 
     _condVar.notify_one(); 
    } 

    bool try_lock() 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     if (0 == _count) 
     return false; 

     _count--; 
     return true; 
    } 

    template <typename Duration> 
    bool try_lock_for(const Duration & duration) 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     if (!_condVar.wait_for(local_lock, duration, getWaitPredicate())) 
     return false; 

     _count--; 
     return true; 
    } 

    template <class TimePoint> 
    bool try_lock_until(const TimePoint & timePoint) 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     if (!_condVar.wait_until(local_lock, timePoint, getWaitPredicate())) 
     return false; 

     _count--; 
     return true; 
    } 

    template <class WaitCriteria> 
    bool timed_lock(const WaitCriteria & criteria) 
    { 
     boost::unique_lock<boost::mutex> local_lock(_mutex); 
     if (!_condVar.timed_wait(local_lock, criteria, getWaitPredicate())) 
     return false; 

     _count--; 
     return true; 
    } 
}; 
相關問題