Как мне добиться чего-то похожего на семафор, используя 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;
}
};