16

スレッド A に次のコードがあります。pthread_cond_wait()

pthread_mutex_lock(&my_lock);     
if ( false == testCondition )        
    pthread_cond_wait(&my_wait,&my_lock); 
pthread_mutex_unlock(&my_lock);

スレッドBに次のコードがあり、スレッドAに通知します

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_cond_signal(&my_wait);
pthread_mutex_unlock(&my_lock);

他にスレッドがない場合、pthread_cond_signal(&my_wait)以下に示すようにクリティカル セクション ブロックの外に移動しても違いはありますか?

pthread_mutex_lock(&my_lock);  
testCondition = true;
pthread_mutex_unlock(&my_lock);
pthread_cond_signal(&my_wait);
4

5 に答える 5

18

私の推奨事項は、通常、pthread_cond_signal()呼び出しをロックされた領域内に保持することですが、おそらくあなたが考える理由からではありません.

ほとんどの場合、pthread_cond_signal()ロックを保持した状態で呼び出すかどうかは問題ではありません。ベンは、別のスレッドが待機している場合、ロックが解放されたときに一部のスケジューラがコンテキストの切り替えを強制する可能性があるため、スレッドが呼び出される前に切り替えられる可能性があることは正しいですpthread_cond_signal()。一方、一部のスケジューラーは、 を呼び出すとすぐに待機中のスレッドを実行するpthread_cond_signal()ため、ロックを保持したまま呼び出すと、待機中のスレッドが起動し、すぐにスリープ状態に戻ります (ミューテックスでブロックされているため)。シグナリングスレッドがロックを解除するまで。正確な動作は実装固有であり、オペレーティング システムのバージョンによって異なる可能性があるため、信頼できるものではありません。

しかし、これらはすべて、コードの可読性と正確性という最大の懸念事項を無視しています。この種のマイクロ最適化によって実際のパフォーマンスが向上する可能性はほとんどありません (最初にプロファイルを作成し、2 番目に最適化するという最適化の最初のルールを思い出してください)。ただし、待機中のスレッドのセットが、条件を設定してシグナルを送信するポイントの間で変更できないことがわかっていると、制御フローについて考えるのが簡単になります。それ以外の場合は、次のようなことを考える必要があります。「スレッド AtestCondition=TRUEがロックを設定して解放し、次にスレッド B が実行されてそれtestConditionが true であることを確認すると、 をスキップしpthread_cond_wait()て にリセットtestConditionFALSE、最後にスレッド A が実行されて呼び出されます。pthread_cond_signal()、これはスレッド B が実際には待機していなかったが、testConditionもはや真実ではないため、スレッド C をウェイクアップします」.これにより、条件の設定とシグナルの送信が相互にアトミックであることがわかります。

関連して、あなたの呼び方pthread_cond_wait()は間違っています。条件変数が実際にシグナルされずに が戻る可能性は (まれpthread_cond_wait()ではありますが) あります。また、条件が true でなくても、シグナルが最終的にスレッドを呼び起こす可能性がある他のケース (たとえば、上記のレース) があります。安全を確保するために、条件をテストするループpthread_cond_wait()内に呼び出しを配置する必要があります。これにより、ロックを再取得した後に条件が満たされない場合に再度呼び出すことができます。あなたの例では、次のようになります。while()pthread_cond_wait()

pthread_mutex_lock(&my_lock);     
while ( false == testCondition ) {
    pthread_cond_wait(&my_wait,&my_lock);
}
pthread_mutex_unlock(&my_lock);

(また、元の例のおそらくタイプミスを修正しました。これはmy_mutexpthread_cond_wait()呼び出しの代わりに を使用していますmy_lock。)

于 2009-11-07T05:36:41.840 に答える
2

条件変数を待機しているスレッドはミューテックスをロックしたままにしておく必要があり、他のスレッドは常にミューテックスをロックした状態でシグナルを送る必要があります。このようにして、シグナルを送信したときに、他のスレッドが条件を待っていることがわかります。そうしないと、待機中のスレッドがシグナル状態が表示されず、無期限にブロックされて待機する可能性があります。

条件変数は通常、次のように使用されます。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int go = 0;

void *threadproc(void *data) {
    printf("Sending go signal\n");
    pthread_mutex_lock(&lock);
    go = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);
}

int main(int argc, char *argv[]) {
    pthread_t thread;
    pthread_mutex_lock(&lock);
    printf("Waiting for signal to go\n");
    pthread_create(&thread, NULL, &threadproc, NULL);
    while(!go) {
        pthread_cond_wait(&cond, &lock);
    }
    printf("We're allowed to go now!\n");
    pthread_mutex_unlock(&lock);
    pthread_join(thread, NULL);
    return 0;
}

これは有効です:

void *threadproc(void *data) {
    printf("Sending go signal\n");
    go = 1;
    pthread_cond_signal(&cond);
}

しかし、何が起こっているかを考えてみましょうmain

while(!go) {
    /* Suppose a long delay happens here, during which the signal is sent */
    pthread_cond_wait(&cond, &lock);
}

そのコメントで説明されている遅延が発生した場合は、pthread_cond_waitおそらく永遠に待たされます。これが、ミューテックスをロックしてシグナルを送る理由です。

于 2009-11-19T03:46:01.883 に答える
1

どちらも正しいですが、反応性の問題のために、ほとんどのスケジューラはロックが解除されると別のスレッドに手を差し伸べます。ロックを解除する前にシグナルを送らないと、待機中のスレッド A が準備完了リストにないため、B が再度スケジュールされて pthread_cond_signal() を呼び出すまで、スレッドはスケジュールされません。

于 2009-10-28T22:07:49.430 に答える
0

条件変数については、次の記事を参照してください: Techniques for Improving the Scalability of Applications Using POSIX Thread Condition Variables (「Avoiding the Mutex Contention」セクションとポイント 7 を参照)

2番目のバージョンには、パフォーマンス上の利点がある可能性があると書かれています。pthread_cond_wait を持つスレッドが待機する頻度が減るためです。

于 2012-12-20T13:14:43.640 に答える
0

Open Group Base Specifications Issue 7 IEEE Std 1003.1, 2013 Edition (私が知る限り、これは公式の pthread 仕様です) は、この問題について次のように述べています。

pthread_cond_broadcast() または pthread_cond_signal() 関数は、スレッドが待機中に pthread_cond_wait() または pthread_cond_timedwait() を呼び出すスレッドが条件変数に関連付けたミューテックスを現在所有しているかどうかにかかわらず、スレッドによって呼び出すことができます。ただし、予測可能なスケジューリング動作が必要な場合、そのミューテックスは pthread_cond_broadcast() または pthread_cond_signal() を呼び出すスレッドによってロックされます。

私の個人的な経験を追加するために、私は、起動されたスレッドによって条件変数が破棄された (およびそれを含むメモリが解放された) コードを持つアプリケーションで作業していました。マルチコア デバイス (iPad Air 2) では、pthread_cond_signal() がミューテックス ロックの外側にある場合、pthread_cond_signal が完了する前にウェイターがウェイクアップして条件変数を破棄したため、実際にクラッシュすることがあることがわかりました。これはかなり予想外でした。

したがって、私は間違いなく「ロック内の信号」バージョンの方に向きを変えます。それはより安全に見えます.

于 2016-04-21T15:44:14.263 に答える