Как мне добиться чего-то похожего на семафор, используя boost в C++?

Я заметил, что boost не поддерживает семафоры. Какой самый простой способ добиться подобного эффекта?

3 ответа

Решение

Вам нужны либо семафоры Boost Interprocess, либо примитивы синхронизации Boost Thread.

Mutex / Lock и условие - это примитивы, которые обычно используются для синхронизации доступа к общим ресурсам в нескольких потоках одного процесса. Существуют эксклюзивные, читатель-писательские и рекурсивные / реентерабельные типы мьютексов. Другими словами, Mutex - это эксклюзивный замок. Условие используется для достижения атомарности, когда вам нужно разблокировать мьютекс и ждать, пока объект изменится. Когда вы начинаете ожидать условие, оно разблокирует мьютекс и гарантирует, что unlock + call to wait является атомарным, и никакие другие потоки не могут изменять ресурс между этими двумя операциями.

Семафор, в другом случае, представляет собой сочетание условия и мьютекса и используется для той же цели, но для синхронизации доступа между процессами.

См. Мьютекс против Семафора.

Существует также такая вещь, как неблокирующая / без блокировки синхронизация, которая становится очень популярной в наши дни. Я лично использую его в высокочастотных торговых приложениях, когда объем данных относительно велик, а низкая задержка имеет большое значение.

В вашем случае, я предполагаю, что 5 философов могут пообедать в одном процессе с 5 нитями. В этом случае вы должны использовать мьютекс, а не семафор. Вы можете или не можете использовать условие, хотя. Это зависит от того, что именно и как именно вы хотите реализовать эту обеденную процедуру.

Я не уверен, как описать это лучше, так как в итоге я напишу об этом книгу. Поэтому я бы порекомендовал вам найти книгу, которая уже написана, чтобы понять основные понятия. Зная основы, вы можете использовать API / библиотеки / фреймворки, такие как POSIX-потоки, Boost Interprocess или Thread, ACE или даже неблокирующие алгоритмы, чтобы достичь того, что вы хотите.

Удачи!

Это один из способов реализации очень простого семафора с использованием Boost.Thread. Это межпоточный семафор, а не межпроцессный. Никаких гарантий и т. Д. - я даже не скомпилировал код. Он иллюстрирует взаимодействие мьютексов и условных переменных и предполагает относительно недавнюю версию Boost.

Обратите внимание, как мьютекс и переменная условия "спарены" - потоки должны иметь блокировку мьютекса, чтобы ожидать переменную условия, и повторно получить блокировку, когда они проснутся. Кроме того, код, который изменяет данные, должен явно пробуждать другой код, который может ожидать. Это означает, что мьютекс, переменная условия, данные и условия, которые вызывают активацию, тесно связаны. Тесная связь также означает, что данные, мьютекс и переменная условия должны быть инкапсулированы, если это возможно, - любая внешняя модификация может нарушать код странным образом, включая взаимоблокировки, пропущенные пробуждения и другие странные ошибки.

Все это действительно является дополнением к ответу Влада Лазаренко - понимание теории и принципов, по крайней мере, так же важно, как наличие "рабочего" кода в многопоточном программировании.

#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_;
    }

};

Я сделал класс семафора совместимым с бустами 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;
   }
};
Другие вопросы по тегам