pthreads
条件変数
サーチ…
前書き
条件変数は、スレッドが別のスレッドで起こったことを待つ場合に便利です。例えば、1つまたは複数の生成スレッドおよび1つの消費スレッドを有するプロデューサ/コンシューマシナリオでは、新しいデータが利用可能であることを消費スレッドに通知するために条件変数を使用することができる。
備考
一般的なプロセス
条件付き変数(プロデューサ/コンシューマの例ではqueueCond)は常にミューテックス(プロデューサ/コンシューマの例ではqueueMutex)に結合され、常に「通常の」状態変数にも結合する必要があります(queue.empty )をプロデューサ/コンシューマの例で使用します)。これを正しく使用すると、消費者に新しいデータが欠けていないことが保証されます。
一般的には、プロセスは次のようにする必要があります。
- シグナルスレッドの場合:
- ミューテックスをロックする
- データと状態変数を更新する
- 条件変数に信号を送る
- ミューテックスのロックを解除する
- 待機スレッドの場合:
- ミューテックスをロックする
- データが準備ができていない限りループして状態変数
whileループします -
whileループでは、pthread_cond_wait()条件変数を待つか、 -
whileループが終了すると、新しいデータが準備され、mutexがロックされていることが確認されます - データで何かをする
- ミューテックスのロックを解除し、繰り返してください
このスキームを使用すると、シグナリングおよび待機スレッドがスケジュールされている場合でも、待機中のスレッドはデータを失うことはありません(有効なデータを準備して永久に待機することはありません)。これは、シグナルスレッドのステップを手動で実行し、待機中のスレッドの各ステップに対してmutex、状態、および状態変数の状態を記録することによって実現できます。
pthread_cond_waitとミューテックス
上記の処理を容易にするためには、mutexをロックした状態でpthread_cond_wait()を呼び出す必要があります。呼び出されると、 pthread_cond_wait()はスレッドをスリープ状態にする前にmutexのロックを解除し、何らかの理由で戻ってくる直前にmutexが再ロックされます。これはまた、他のスレッドが現在mutexをロックしている場合、 pthread_cond_wait()はmutexのロックが解除されるのを待って、待機中のスレッドが実際にmutexを取得できるまで、ロックしようとする他のスレッド同時にミューテックス。
偽の起床
また、状態変数を待っているwhileループが単純なif文の代わりに使用できるように見えるかもしれません。しかし、Posix標準では、 pthread_cond_wait()が実際にシグナルを出すことなく待機中にいわゆる "擬似"ウェイクアップを行うことができるため、 whileループが必要です。したがって、コードは、 pthread_cond_wait()が実際に通知されたか、またはこれらの偽の起床のために返されたかどうかを確認するために状態変数を再確認する必要があります。
プロデューサー/消費者の例
pthread_mutex_t queueMutex;
pthread_cond_t queueCond;
Queue queue;
void Initialize() {
//Initialize the mutex and the condition variable
pthread_mutex_init(&queueMutex, NULL);
pthread_cond_init(&queueCond, NULL);
}
void Producer() {
//First we get some new data
Data *newData = MakeNewData();
//Lock the queue mutex to make sure that adding data to the queue happens correctly
pthread_mutex_lock(&queueMutex);
//Push new data to the queue
queue.push(newData);
//Signal the condition variable that new data is available in the queue
pthread_cond_signal(&queueCond);
//Done, unlock the mutex
pthread_mutex_unlock(&queueMutex);
}
void Consumer() {
//Run the consumer loop
while(1) {
//Start by locking the queue mutex
pthread_mutex_lock(&queueMutex);
//As long as the queue is empty,
while(queue.empty()) {
// - wait for the condition variable to be signalled
//Note: This call unlocks the mutex when called and
//relocks it before returning!
pthread_cond_wait(&queueCond, &queueMutex);
}
//As we returned from the call, there must be new data in the queue - get it,
Data *newData = queue.front();
// - and remove it from the queue
queue.pop();
//Now unlock the mutex
pthread_mutex_unlock(&queueMutex);
// - and process the new data
ProcessData(newData);
}
}