条件変数を保護するにはミューテックスが必要だと言われています。
ここでは、値がシグナリングと待機を決定する
通常の共有変数としてpthread_cond_t
宣言された実際の条件変数への参照です。
?count
条件変数を保護するにはミューテックスが必要だと言われています。
ここでは、値がシグナリングと待機を決定する
通常の共有変数としてpthread_cond_t
宣言された実際の条件変数への参照です。
?count
ここでの参照は、pthread_cond_t として宣言された実際の状態変数への参照ですか、それとも値がシグナル伝達と待機を決定する通常の共有変数カウントですか?
どちらも参考になります。
ミューテックスは、共有変数(count
質問内)をチェックできるようにし、その変数の値が目的の条件を満たさない場合、内部で実行される待機はpthread_cond_wait()
そのチェックに関してアトミックに発生します。
ミューテックスで解決される問題は、アトミックである必要がある 2 つの別個の操作があることです。
count
pthread_cond_wait()
条件がまだ満たされていない場合は内部で待機します。Apthread_cond_signal()
は「持続」pthread_cond_t
しません。オブジェクトを待機しているスレッドがない場合、シグナルは何もしません。したがって、上記の 2 つの操作を相互にアトミックにするミューテックスがなければ、次のような状況に陥る可能性があります。
count
は非ゼロスレッド B は、インクリメントするとシグナルを送信します(ゼロ以外count
に設定されます)。count
count
して、それがゼロであることを発見しますpthread_cond_wait()
、スレッド "B" が来てcount
1 にインクリメントし、 を呼び出しますpthread_cond_signal()
。「A」はpthread_cond_t
まだオブジェクトを待っていないため、この呼び出しは実際には何の影響もありません。pthread_cond_wait()
が、条件変数のシグナルが記憶されていないため、この時点でブロックし、既に出入りしたシグナルを待ちます。ミューテックス (すべてのスレッドがルールに従っている限り) は、アイテム #2 がアイテム 1 と 3 の間に発生しないようにします。スレッド "B" がインクリメントする唯一の方法count
は、A が参照する前count
または参照した後です「A」はすでに信号を待っています。
スレッドが条件変数で待機する準備をし、最初のスレッドが実際に待機する直前に別のスレッドが条件を通知するという競合状態を回避するために、条件変数は常にミューテックスに関連付ける必要があります。
詳細はこちら
いくつかのサンプル:
スレッド 1 (条件を待機)
pthread_mutex_lock(cond_mutex);
while(i<5)
{
pthread_cond_wait(cond, cond_mutex);
}
pthread_mutex_unlock(cond_mutex);
スレッド 2 (状態を通知する)
pthread_mutex_lock(cond_mutex);
i++;
if(i>=5)
{
pthread_cond_signal(cond);
}
pthread_mutex_unlock(cond_mutex);
上記と同じように、mutex は条件の原因である変数 'i' を保護します。条件が満たされていないことがわかると、暗黙的にミューテックスを解放する条件待機に入り、それによってシグナリングを行うスレッドがミューテックスを取得して「i」で作業し、競合状態を回避できるようにします。
さて、あなたの質問によると、シグナルスレッドが最初にシグナルを送信する場合、そうする前にミューテックスを取得する必要があります。 2 番目のスレッドは既にシグナルを送信しており、その後は誰もシグナルを送信せず、最初のスレッドは永遠に待機し続けます。したがって、この意味で、mutex は条件変数と条件変数の両方に使用されます。
より良いユースケースは、条件変数とそれに関連するミューテックスをよりよく説明するのに役立つかもしれないと考えました.
Barrier Syncと呼ばれるものを実装するために posix 条件変数を使用します。基本的に、すべて同じことを行う 15 個の (データ プレーン) スレッドがあるアプリで使用し、すべてのデータ プレーンの初期化が完了するまですべてのスレッドを待機させます。すべての (内部) データ プレーンの初期化が完了すると、データの処理を開始できます。
これがコードです。この特定のアプリケーションではテンプレートを使用できなかったため、Boost からアルゴリズムをコピーしたことに注意してください。
void LinuxPlatformManager::barrierSync()
{
// Algorithm taken from boost::barrier
// In the class constructor, the variables are initialized as follows:
// barrierGeneration_ = 0;
// barrierCounter_ = numCores_; // numCores_ is 15
// barrierThreshold_ = numCores_;
// Locking the mutex here synchronizes all condVar logic manipulation
// from this point until the point where either pthread_cond_wait() or
// pthread_cond_broadcast() is called below
pthread_mutex_lock(&barrierMutex_);
int gen = barrierGeneration_;
if(--barrierCounter_ == 0)
{
// The last thread to call barrierSync() enters here,
// meaning they have all called barrierSync()
barrierGeneration_++;
barrierCounter_ = barrierThreshold_;
// broadcast is the same as signal, but it signals ALL waiting threads
pthread_cond_broadcast(&barrierCond_);
}
while(gen == barrierGeneration_)
{
// All but the last thread to call this method enter here
// This call is blocking, not on the mutex, but on the condVar
// this call actually releases the mutex
pthread_cond_wait(&barrierCond_, &barrierMutex_);
}
pthread_mutex_unlock(&barrierMutex_);
}
メソッドに入るすべてのスレッドがbarrierSync()
ミューテックスをロックすることに注意してください。これにより、ミューテックス ロックといずれかの呼び出しの間のすべてがpthread_cond_wait()
アトミックpthread_mutex_unlock()
になります。また、ここで説明したようにミューテックスが解放/ロック解除されていることにも注意してください。このリンクでは、最初にミューテックスをロックせずに呼び出した場合の動作は未定義であることにも言及しています。pthread_cond_wait()
pthread_cond_wait()
pthread_cond_wait()
がミューテックス ロックを解放しなかった場合pthread_mutex_lock()
、メソッドの開始時にすべてのスレッドが への呼び出しでブロックされ、変数をアトミックに (またはスレッド セーフな方法で)barrierSync()
減らすことbarrierCounter_
も (関連する変数を操作することも) 知ることができなくなります。呼び出したスレッドの数barrierSync()
したがって、これらすべてを要約すると、条件変数に関連付けられたミューテックスは、条件変数自体を保護するために使用されるのではなく、条件 ( など) に関連付けられたロジックをアトミックbarrierCounter_
かつスレッドセーフにするために使用されます。スレッドが条件が真になるのを待ってブロックする場合、実際には関連するミューテックスではなく、条件付き変数でブロックしています。を呼び出すと、pthread_cond_broadcast/signal()
それらのブロックが解除されます。
追加の参照用に、pthread_cond_broadcast()
関連する別のリソースを次に示します。pthread_cond_signal()