ブーストがセマフォをサポートしていないように見えることに気づきました。同様の効果を達成するための最も簡単な方法は何ですか?
3 に答える
これは、Boost.Threadを使用して非常に単純なセマフォを実装する1つの方法です。これはスレッド間セマフォであり、プロセス間セマフォではありません。黙示の保証などはありません。コードをコンパイルしていません。これは、ミューテックスと条件変数がどのように相互作用するかを示しており、かなり新しいバージョンの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_;
}
};
BoostInterprocessセマフォまたはBoostThread同期プリミティブのいずれかが必要です。
ミューテックス/ロックと条件は、単一プロセスの複数のスレッド間で共有リソースへのアクセスを同期するために一般的に使用されるプリミティブです。排他的なリーダーライタータイプと再帰/リエントラントタイプのミューテックスがあります。つまり、Mutexは排他ロックです。条件は、ミューテックスのロックを解除してオブジェクトが変更されるのを待つ必要がある場合に、原子性を実現するために使用されます。条件で待機を開始すると、ミューテックスのロックが解除され、ロック解除+待機の呼び出しがアトミックであり、他のスレッドがこれら2つの操作間でリソースを変更できないことが保証されます。
別のケースでは、セマフォは条件とミューテックスの組み合わせであり、まったく同じ目的で使用されますが、プロセス間でアクセスを同期するために使用されます。
MutexとSemaphoreを参照してください。
最近非常に人気が高まっているノンブロッキング/ロックフリー同期などもあります。私は、データ量が比較的多く、低遅延が非常に重要である高頻度取引アプリケーションで個人的に使用しています。
あなたの場合、5人の哲学者が5つのスレッドを持つ単一のプロセス内で夕食をとることができると思います。その場合、セマフォではなくミューテックスを使用する必要があります。ただし、条件を使用する場合と使用しない場合があります。それはあなたがその食事の手順を正確にそしてどれだけ正確に実行したいかに依存します。
私はそれについて本を書くことになるので、それをよりよく説明する方法がわかりません。ですから、基本的な概念を理解するためにすでに書かれている本を見つけることをお勧めします。基本を理解したら、POSIXスレッド、Boost InterprocessまたはThread、ACE、さらには非ブロッキングアルゴリズムなどのAPI/ライブラリ/フレームワークを使用して目的を達成できます。
幸運を!
ブーストの概念と互換性のあるセマフォクラスを作成したTimedLockable
ので、のようなロックで使用できますboost::unique_lock<semaphore>
。これは、1つの古典的な定義ではセマフォではありませんが、1つとして使用できます。それでも、それが誰かに役立つことを願っています。
それはどういうわけかテストされましたが、私が何か間違ったことをした可能性が高いです。誰かがそれが正しいことを証明できれば素晴らしいでしょう。
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;
}
};