2010-10-13 59 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; 
    } 
}; 
相关问题