web-dev-qa-db-ja.com

pthreadの待機条件の周りにwhileループが必要なのはなぜですか?

私はpthreadと待機状態を学んでいます。私が知る限り、典型的な待機スレッドは次のようになります。

pthread_mutex_lock(&m);
while(!condition)
     pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

理解できないのは、while(!condition)を使用してスレッドをウェイクアップしても、pthread_cond_signal()行が必要な理由です。

pthread_cond_broadcast()を使用する場合、条件をテストする必要があることを理解できます。なぜなら、all待機中のスレッドで、そのうちの1つがミューテックスのロックを解除する前に条件を再び偽にできるからです(したがって、その時点で実行すべきではない別のウェイクアップされたスレッドに実行を転送します)。しかし、pthread_cond_signal()を使用する場合は、oneスレッドだけでウェイクアップするため、条件は真でなければなりません。したがって、コードは次のようになります。

pthread_mutex_lock(&m);
pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

発生する可能性のあるスプリアス信号について読みました。これが(そしてこれだけ)理由ですか?なぜ偽の単音が必要なのですか?または、私が得られない何かがありますか?

信号コードは次のようになります。

pthread_mutex_lock(&m);
condition = true;
pthread_cond_signal(&cond); // Should wake up *one* thread
pthread_mutex_unlock(&m);
50
Emiliano

Whileループにpthread_cond_waitを配置する必要がある本当の理由は、誤ったウェイクアップのためではありません。条件変数に誤ったウェイクアップがない場合でも、一般的なタイプのエラーをキャッチするにはループが必要です。どうして?複数のスレッドが同じ条件で待機する場合に何が起こるか考えてください:

Thread 1                         Thread 2           Thread 3
check condition (fails)
(in cond_wait) unlock mutex
(in cond_wait) wait
                                 lock mutex
                                 set condition
                                 signal condvar
                                 unlock mutex
                                                    lock mutex
                                                    check condition (succeeds)
                                                    do stuff
                                                    unset condition
                                                    unlock mutex
(in cond_wait) wake up
(in cond_wait) lock mutex
<thread is awake, but condition
is unset>

ここでの問題は、待機する前にスレッドがミューテックスを解放しなければならないことであり、潜在的にそのスレッドが待っていたものを「盗む」ことができます。 1つのスレッドのみがその条件で待機できることが保証されていない限り、スレッドがウェイクアップするときに条件が有効であると想定するのは誤りです。

47
Chris Lu

状態をチェックしないと仮定します。それから通常、次の悪いことが起こるのを避けることはできません(少なくとも、1行のコードでそれを避けることはできません):

 Sender                             Receiver
locks mutex
sets condition
signals condvar, but nothing 
  is waiting so has no effect
releases mutex
                                    locks mutex
                                    waits. Forever.

もちろん、2番目のコード例では、これを回避できます。

pthread_mutex_lock(&m);
if (!condition) pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

そうすると、たった1つのレシーバーしか存在しない場合、およびcond_signalは、それを起動できる唯一のものであり、条件が設定されたときにのみ起動するため、ループは必要ありません。 nosは、2番目の「if」が正しくない理由をカバーしています。

16
Steve Jessop