8

我注意到 boost 似乎不支持信号量。达到类似效果的最简单方法是什么?

4

3 回答 3

22

这是使用 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_;
    }

};
于 2010-10-14T03:29:29.303 回答
7

您要么需要Boost Interprocess 信号量,要么需要Boost Thread 同步原语。

互斥锁/锁条件是通常用于跨单个进程的多个线程同步访问共享资源的原语。互斥体有排他性读写器递归/可重入类型。换句话说,互斥锁是一种排他锁。当您需要解锁互斥锁并等待对象更改时,使用条件来实现原子性。当您开始等待某个条件时,它会解锁互斥锁并保证解锁 + 等待调用是原子的,并且没有其他线程可以在这两个操作之间修改资源。

在另一种情况下,信号量是条件和互斥体的混合体,用于完全相同的目的,但用于跨进程同步访问。

请参阅互斥量与信号量

如今,非阻塞/无锁同步也变得非常流行。我个人在高频交易应用程序中使用它,当数据量相对非常大并且低延迟确实很重要时。

在您的情况下,我假设 5 个哲学家可以在具有 5 个线程的单个进程中共进晚餐。在这种情况下,您必须使用互斥锁,而不是信号量。不过,您可能会也可能不会使用条件。这取决于您想要执行该用餐程序的具体内容和方式。

我不确定如何更好地描述它,因为我最终会写一本关于它的书。所以我建议你找一些已经写好的书来理解基本概念。一旦您了解了基础知识,您就可以使用 API/库/框架,如POSIX 线程Boost InterprocessThreadACE甚至非阻塞算法来实现您想要的。

祝你好运!

于 2010-10-13T23:28:25.537 回答
0

我制作了一个与 boosts 概念兼容的信号量类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;
   }
};
于 2015-03-19T08:52:08.333 回答