なぜpthread_cond_waitに偽のウェイクアップがあるのですか?


145

manページを引用するには:

条件変数を使用する場合、各条件に関連付けられた共有変数を含むブール述語が常にあり、スレッドが続行する場合はtrueになります。pthread_cond_timedwait()またはpthread_cond_wait()関数からの誤ったウェイクアップが発生する場合があります。pthread_cond_timedwait()またはpthread_cond_wait()からの戻りは、この述部の値について何の意味も持たないので、そのような戻り時に述部を再評価する必要があります。

そのため、pthread_cond_wait通知していなくても戻ることができます。少なくとも一見すると、それはかなりひどいようです。これは、間違った値をランダムに返したり、実際に適切なreturnステートメントに到達する前にランダムに返したりする関数のようなものです。大きなバグのようです。しかし、これを修正するのではなくmanページに文書化することを選択したという事実は、pthread_cond_wait誤って目を覚ますことになる正当な理由があることを示しているようです。おそらく、それがどうしようもないようにそれを作るそれがどのように機能するかについて本質的な何かがある。問題は何ですか。

なぜpthread_cond_wait偽って戻るのですか?正しく通知された場合にのみウェイクアップすることを保証できないのはなぜですか?誰かがその偽の振る舞いの理由を説明できますか?


5
プロセスがシグナルをキャッチするたびに戻ることと関係があると思います。ほとんどの* nixは、シグナルが割り込んだ後でブロッキング呼び出しを再開しません。シグナルが発生したことを示すエラーコードを設定/返すだけです。
cHao

1
@cHao:条件変数には偽のウェイクアップが発生する他の理由があるため、シグナルの処理はエラーではないことに注意してくださいpthread_cond_(timed)wait:「シグナルが配信された場合...中断されないか、または疑似ウェイクアップのためにゼロを返します。」他のブロッキング関数はEINTR、信号によって中断されたとき(たとえばread)、または再開する必要があるとき(たとえばpthread_mutex_lock)を示します。したがって、偽のウェイクアップの理由が他にない場合はpthread_cond_wait、それらのいずれかと同じように定義できます。
スティーブジェソップ

4
ウィキペディアの関連記事:スプリアスウェイクアップ
Palec、2015年


多くの関数は、完全にジョブを完全に実行することはできず(中断されたI / O)、監視関数は、変更がキャンセルされた、または元に戻されたディレクトリへの変更などの非イベントを受信できます。どうしたの?
curiousguy

回答:


77

次の説明は、「POSIXスレッドを使用したプログラミング」(p。80)のDavid R. Butenhofによって行われました。

スプリアスウェイクアップは奇妙に聞こえるかもしれませんが、一部のマルチプロセッサシステムでは、条件ウェイクアップを完全に予測可能にすると、すべての条件変数操作が大幅に遅くなる可能性があります。

次のcomp.programming.threadsの議論で、彼は設計の背後にある考え方をさらに詳しく説明します。

パトリック・ドイルはこう書いている: 
>記事の中で、トムペインは次のように書いています。 
>> Kaz Kylhekuが書いた: 
>>:実装が挿入を避けられない場合があるためです 
>>:これらの偽のウェイクアップ。それらを防ぐにはコストがかかる可能性があります。

>>しかし、なぜですか?なぜこれがそんなに難しいのですか?たとえば、私たちが話しているのは
>>シグナルが到着したときに待機がタイムアウトする状況? 

>ご存知のとおり、pthreadの設計者は次のようなロジックを使用したのでしょうか。 
>条件変数のユーザーは、終了時に条件を確認する必要があります。 
>許可する場合、追加の負担はありません 
>偽のウェイクアップ。そして、偽りを許すことは考えられるので
>ウェイクアップは、実装をより高速にすることができます。それが役立つのは、 
>それらを許可します。 

>彼らは、特定の実装を心に留めていなかったかもしれません。 

あなたはそれを十分に押し込まなかったことを除いて、あなたは実際には全く遠くないです。 

その意図は、述語ループを要求することにより、正しい/堅牢なコードを強制することでした。これは。。。でした
中の「コアスレッディ」の間で証明可能な正しい学術的条件によって駆動されます 
ワーキンググループ、しかし私は誰もが意図に本当に同意しなかったとは思いませんが 
彼らがそれが何を意味するかを理解したら 

私たちはいくつかのレベルの正当化でその意図に従いました。最初はそれでした
ループを「信心深く」使用して、アプリケーションをそれ自体の不完全さから保護します 
コーディング慣行。2つ目は、抽象的に想像することは難しくなかったことです。
この要件を悪用して改善できるマシンと実装コード 
最適化による平均条件待機操作のパフォーマンス 
同期メカニズム。 
/ ------------------ [David.Buten ... @ compaq.com] ------------------ \ 
| Compaq Computer Corporation POSIXスレッドアーキテクト|
| 私の本:http://www.awl.com/cseng/titles/0-201-63392-2/ |
\ ----- [http://home.earthlink.net/~anneart/family/dave.html] ----- / 


22
基本的にこれは何も言いません。「それはより速くなるかもしれない」という当初の考え以外はここでは何も説明されていませんが、それがどのように、またはそれができるかは誰にもわかりません。
Bogdan Ionitza

107

「偽のウェイクアップ」が意味する可能性があることは少なくとも2つあります。

  • ブロックされたスレッドは、条件pthread_cond_waitへの呼び出しpthread_call_signalまたはpthread_cond_broadcast条件が発生していなくても、呼び出しから戻ることができます。
  • またはpthread_cond_waitへの呼び出しが原因でスレッドが返されてブロックされましたが、ミューテックスを再取得した後、基になる述語が真ではなくなったことがわかりました。pthread_cond_signalpthread_cond_broadcast

ただし、条件変数の実装で前者のケースが許可されていない場合でも、後者のケースが発生する可能性があります。プロデューサーコンシューマーキューと3つのスレッドについて考えます。

  • スレッド1が要素をキューから取り出し、ミューテックスを解放したところ、キューは空になりました。スレッドは、CPUで取得した要素を使って何をしているのですか。
  • スレッド2は要素をデキューしようとしますが、ミューテックスでチェックされたときにキューが空であることを検出し、を呼び出しpthread_cond_wait、シグナル/ブロードキャストを待機しているコールをブロックします。
  • スレッド3はミューテックスを取得し、新しい要素をキューに挿入して、条件変数に通知し、ロックを解放します。
  • スレッド3からの通知に応答して、条件で待機していたスレッド2が実行されるようにスケジュールされます。
  • ただし、スレッド2がCPUにアクセスしてキューロックを取得する前に、スレッド1は現在のタスクを完了し、さらに作業を行うためにキューに戻ります。キューのロックを取得し、述語をチェックして、キューに作業があることを見つけます。スレッド3が挿入したアイテムのデキューに進み、ロックを解除し、スレッド3がエンキューしたアイテムに対して何をするかを行います。
  • スレッド2はCPUを取得してロックを取得しますが、述語をチェックすると、キューが空であることがわかります。スレッド1がアイテムを「盗んだ」ので、ウェイクアップが誤っているように見えます。スレッド2は、条件を再度待機する必要があります。

したがって、ループの下で述語を常にチェックする必要があるため、基礎となる条件変数が他の種類の偽のウェイクアップを持つ可能性があっても、違いはありません。


23
はい。基本的に、これは、カウントを伴う同期メカニズムの代わりにイベントが使用されるときに発生することです。悲しいことに、POSIXセマフォ(とにかくLinux上)は、スプリアスウェイクアップの影響も受けるようです。同期プリミティブの基本的な機能障害が「通常」として受け入れられ、ユーザーレベルで回避する必要があるのは少し奇妙なことです。 'Spurious segfault'セクション、または 'Spurious connections to the wrong URL'または 'Spurious open of the wrong file'を含む
Martin James

2
「疑似ウェイクアップ」のより一般的なシナリオは、おそらくpthread_cond_broadcast()の呼び出しの副作用です。5つのスレッドのプールがあり、2つがブロードキャストに目覚めて作業を行うとします。他の3人は目を覚まし、作業が完了したことを確認します。マルチプロセッサシステムでは、偶発的に複数のスレッドを起動する条件付き信号が発生する可能性もあります。コードは述語を再度チェックし、無効な状態を確認して、スリープ状態に戻ります。どちらの場合でも、述語をチェックすると問題が解決します。IMOは一般に、生のPOSIXミューテックスや条件文を使用しないでください。
CubicleSoft 2016年

1
@MartinJames-古典的な「偽の」EINTRはどうですか?ループでEINTRを常にテストするのは少し面倒でコードが見苦しくなりますが、開発者はランダムな破損を避けるためにとにかくそれを行うことに同意します。
CubicleSoft 2016年

2
@Yolaいいえ、できません。周辺のmutexをロックするpthread_cond_signal/broadcastことになっているため、を呼び出してmutexをロック解除するまでロックできませんpthread_cond_wait
a3f 16

1
この回答の例は非常に現実的であり、述語をチェックすることをお勧めします。ただし、問題のあるステップ「スレッド1が現在のタスクを完了し、キューに戻ってさらに作業する」を実行し、それを「スレッド1が現在のタスクを完了して待機する」に置き換えることで、同様に適切に修正できませんでした。条件変数」?それは答えで説明されている失敗モードを排除し、疑似ウェイクアップがなければ、コードが正しくなると確信しています。実際に偽のウェイクアップを生成する実際の実装はありますか?
Quuxplusone 2017

7

pthread_cond_signalの「条件信号による複数の目覚め」セクションには、pthread_cond_waitとpthread_cond_signalの実装例があり、偽のwakekupsが関係しています。


2
この答えは間違っていると思います。そのページのサンプル実装には、「すべて通知」と同等の「1つ通知」の実装があります。しかし、それは実際に偽のウェイクアップを生成するようには見えません。スレッドがウェイクアップする唯一の方法は、「notify all」を呼び出す別のスレッド、または「-thing-labeled-」「notify one」-which-is-really-「notify all」を呼び出す別のスレッドによる方法です。
Quuxplusone 2017

5

設計時には考慮されていないとは思いますが、実際の技術的な理由は次のとおりです。スレッドのキャンセルと組み合わせて、「誤って」ウェイクするオプションを取ることが絶対に必要になる状況があります。どのような実装戦略が可能であるかについて、非常に非常に強い制約を課す用意があります。

重要な問題は、スレッドがでブロックされている間にキャンセルに作用する場合、pthread_cond_wait副作用はあたかも条件変数のシグナルをまったく消費しなかったかのようになることです。ただし、キャンセルの処理を開始するときにまだ信号を消費していないことを確認することは困難であり、非常に制約があります。また、この段階では、条件変数に信号を「再ポスト」することができない場合があります。の呼び出し元がpthread_cond_signalcondvarを破棄し、それが存在していたメモリを解放したことがすでに正当化されている状況にある。

スプリアスウェイクの許容により、簡単に脱出できます。条件変数でブロックされている間に到着したときにキャンセルに対処し続ける代わりに、すでに信号を消費している可能性がある場合(または、何であれ遅延したい場合)、代わりにスプリアスウェイクが発生したことを宣言できます。成功して戻ります。これはキャンセルの操作にまったく干渉しません。正しい呼び出し元は、次にループしてpthread_cond_wait再び呼び出すときに、保留中のキャンセルに単に作用するだけだからです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.