3

pthread_cond_signalシグナリングスレッドが述語の真理値に影響を与える状態を変更し、条件変数に関連付けられたミューテックスを保持せずに呼び出す状況で条件変数が使用されると仮定しますか?このタイプの使用法は、信号が失われる可能性のある競合状態に常にさらされているというのは本当ですか?

私には、常に明らかな競争があるように思われます。

  1. ウェイターは述語をfalseと評価しますが、待機を開始する前に...
  2. 別のスレッドは、述語を真にする方法で状態を変更します。
  3. 他のスレッドはを呼び出しますがpthread_cond_signal、ウェイターがまだいないため、何もしません。
  4. ウェイタースレッドはpthread_cond_wait、述語が真であることに気付かずに入り、無期限に待機します。

pthread_cond_signalしかし、(A)状態を変更している間だけでなく、呼び出し中にミューテックスが保持されるように、または(B)状態を変更している間ミューテックスが保持されるように状況が変更された場合、これと同じ種類の競合状態が常に存在しますか?呼び出している間だけではありませんpthread_cond_signalか?

上記のベストプラクティスではない使用法の有効な使用法があるかどうか、つまり、競合状態自体を回避するために正しい条件変数の実装でそのような使用法を考慮する必要があるかどうかを知りたいという観点から質問しています。それらはすでに本質的に競合しているため、無視できます。

4

3 に答える 3

2

偽のウェイクアップの可能性以外の理由がない場合、状態はミューテックス内で変更する必要があります。これにより、ライターが状態を書き込んでいる間にリーダーが状態を読み取ってしまう可能性があります。

pthread_cond_signal状態が変更された後はいつでも呼び出すことができます。ミューテックス内にある必要はありません。POSIX は、少なくとも 1 つのウェイターが新しい状態をチェックするために目覚めることを保証します。さらに要点:

  1. 呼び出しpthread_cond_signalは、リーダーが最初にミューテックスを取得することを保証しません。読者が新しいステータスを確認する機会を得る前に、別のライターが入り込む可能性があります。条件変数は、リーダーがすぐにライターに従うことを保証しません (結局のところ、リーダーがいない場合はどうなるでしょうか?)
  2. ロックを解放した後に呼び出す方が実際には優れています。なぜなら、目覚めたばかりのリーダーがすぐにスリープ状態に戻って、ライターがまだ保持しているロックを取得しようとするリスクがないからです。

編集: @DietrichEpp はコメントで適切な指摘をしています。ライターは、リーダーが一貫性のない状態にアクセスできないように状態を変更する必要があります。上記で示したように、条件変数で使用されるミューテックスを取得するか、すべての状態変更がアトミックであることを確認することで、これを行うことができます。

于 2011-09-24T00:22:50.330 に答える
2

ここでの基本的なレースは次のようになります。

THREAD A        THREAD B
Mutex lock
Check state
                Change state
                Signal
cvar wait
(never awakens)

状態の変化、シグナル、またはその両方でロックを取得すると、これを回避できます。スレッド A がクリティカル セクションにあり、ロックを保持している間は、状態変更とシグナルの両方が発生することはありません。

スレッド A がスレッド B にインターリーブする逆のケースを考えれば、問題はありません。

THREAD A        THREAD B
                Change state
Mutex lock
Check state
( no need to wait )
Mutex unlock
                Signal (nobody cares)

したがって、スレッド B が操作全体にわたってミューテックスを保持する必要は特にありません。状態の変化と信号の間の無限に短い間隔でミューテックスを保持する必要があるだけです。もちろん、安全な操作のために状態自体にロックが必要な場合は、状態が変化する間もロックを保持する必要があります。

最後に、ミューテックスを早期に削除しても、ほとんどの場合、パフォーマンスが向上する可能性は低いことに注意してください。ミューテックスを保持することを要求すると、条件変数の内部ロックをめぐる競合が減少し、最新の pthreads 実装では、システムは、待機中のスレッドを cvar での待機からミューテックスでの待機に、ウェイクアップせずに「移動」できます (したがって、それを回避します)。ミューテックスですぐにブロックするためだけにウェイクアップします)。 コメントで指摘されているように、ミューテックスを削除すると、必要なシステムコールの数が減るため、場合によってはパフォーマンスが向上する可能性があります。また、条件変数の内部ミューテックスで余分な競合が発生する可能性もあります。言いにくい。いずれにせよ、心配する価値はおそらくありません。

適用される標準pthread_cond_signalでは、ミューテックスを保持せずに安全に呼び出すことができる必要があることに注意してください。

pthread_cond_signal() または pthread_cond_broadcast() 関数は、pthread_cond_wait() または pthread_cond_timedwait() を呼び出すスレッドが待機中に条件変数に関連付けたミューテックスを現在所有しているかどうかに関係なく、スレッドによって呼び出すことができます [...]

これは通常、条件変数が内部データ構造に対して内部ロックを持っているか、非常に慎重なロックフリー アルゴリズムを使用していることを意味します。

于 2011-09-24T00:23:53.687 に答える
1

答えは、人種が存在するということです。その人種をなくすには、次のようにする必要があります。

/* atomic op outside of mutex, and then: */

pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);

pthread_cond_signal(&c);

とにかく呼び出すときにミューテックスを保持しないため、データの保護は重要ではありませpthread_cond_signalん。

ミューテックスをロックおよびロック解除することで、バリアが作成されたことを確認してください。シグナル送信者がミューテックスを持っているその短い瞬間に、確実性があります。つまり、他のスレッドがミューテックスを持っていないということです。これは、他のスレッドが重要な領域を実行していないことを意味します。

これは、すべてのスレッドがミューテックスを取得して、投稿した変更を検出しようとしているか、その変更を既に検出して実行した (ミューテックスを解放する) か、探しているものを見つけていないことを意味します。アトミックにミューテックスをあきらめてスリープ状態にします(そして、状態で適切に待機することが保証されています)。

ミューテックスのロック/ロック解除がなければ、同期はありません。シグナルは、変更されたアトミック値を認識していないスレッドがそれを待機するためにアトミック スリープに移行しているときに発生することがあります。

したがって、これは、シグナルを送信しているスレッドの観点からミューテックスが行うことです。他の何かからアクセスの原子性を取得できますが、同期は取得できません。

PS私は以前にこのロジックを実装しました。状況は Linux カーネルにありました (独自のミューテックスと条件変数を使用)。

私の状況では、Signaler が共有データのアトミック操作のミューテックスを保持することは不可能でした。なんで?シグナル送信者はカーネルとユーザー間で共有されるバッファー内のユーザー空間で操作を実行し、(場合によっては) カーネルにシステム コールを行ってスレッドをウェイクアップしたためです。ユーザー空間はバッファにいくつかの変更を加えただけで、いくつかの条件が満たされた場合、ioctl.

そのため、ioctl呼び出しでミューテックスのロック/ロック解除を行い、条件変数にヒットしました。これにより、スレッドは、ユーザー空間によって投稿された最新の変更に関連するウェイクアップを見逃すことがなくなりました。

最初は条件変数シグナルだけでしたが、ミューテックスが関与していないと正しくないように見えたので、状況を少し推論し、ミューテックスを単純にロックおよびロック解除して、同期の儀式を排除する必要があることに気付きました。目覚めを失った。

于 2012-03-29T03:39:05.107 に答える