2011-01-11 5 views
0

こんにちは 次のコードを実行すると、他のスレッドが起動する前に、信号スレッドが長時間実行されていることがわかります。なぜですか?シグナルがロックを解除すると直ちに実行される目覚ましスレッドではありませんか?あるいは、スリープしているスレッドをレディキューに戻すのに時間がかかりますか?cond.signalまたはlock.releaseの問題?

#include pthread.h 

#include stdio.h 

#include stdlib.h 

void stupidfunction1(void *arg); 

void stupidfunction2(void *arg); 

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 

pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; 

int thread1count,thread2count; 

int thread1waiting = 0; 

int thread2waiting = 0; 

void main() 

{ 
    printf("Hello World\n"); 

    pthread_t thread1,thread2; 

    int i; 

    thread1count = 0; 

    thread2count = 0; 


    i = pthread_create(&thread1,NULL,&stupidfunction1,NULL); 

    i = pthread_create(&thread2,NULL,&stupidfunction2,NULL); 

    pthread_join(thread1,NULL); 

    pthread_join(thread2,NULL); 

    printf("Done with everythinh"); 


} 


void stupidfunction1(void *arg) 

{ 
int i = 0; 

    for(i = 0;i<50;i++) 
    { 

    thread1count++; 

    pthread_mutex_lock(&mutex1); 

    if((thread1count-thread2count)>5) 

    { 
      thread1waiting = 1; 

       printf("thread1 waiting \n");  

       pthread_cond_wait(&cond1,&mutex1); 

       thread1waiting = 0; 
    } 

     else if((thread2waiting == 1) && abs(thread1count-thread2count)<1) 

    { 

     printf("signalling thread2\n"); 

     pthread_cond_signal(&cond1); 

    } 

    pthread_mutex_unlock(&mutex1); 

    printf("Hey its thread 1 @ %d\n",thread1count); 
    } 
} 


void stupidfunction2(void *arg) 
{ 
int i = 0; 

    for(i = 0;i<50;i++) 
    { 

    thread2count++; 

    pthread_mutex_lock(&mutex1); 

    if((thread2count-thread1count)>5) 
    { 
       thread2waiting = 1; 

      printf("thread2 waiting \n");  

       pthread_cond_wait(&cond1,&mutex1); 

       thread2waiting = 0; 
    } 

     else if((thread1waiting == 1) && abs(thread1count-thread2count)<1) 
    { 

     printf("signalling thread1\n"); 

     pthread_cond_signal(&cond1); 
    } 

    pthread_mutex_unlock(&mutex1); 

    printf("Hey its thread 2 @ %d\n",thread2count); 
    } 
} 

OUTPUT:

Hey its thread 2 @ 1 

Hey its thread 2 @ 2 

Hey its thread 2 @ 3 

Hey its thread 2 @ 4 

Hey its thread 2 @ 5 

thread2 waiting 

Hey its thread 1 @ 1 

Hey its thread 1 @ 2 

Hey its thread 1 @ 3 

Hey its thread 1 @ 4 

Hey its thread 1 @ 5 

signalling thread2 

Hey its thread 1 @ 6 

Hey its thread 1 @ 7 

Hey its thread 1 @ 8 

Hey its thread 1 @ 9 

Hey its thread 1 @ 10 

Hey its thread 1 @ 11 

答えて

0

スレッドは、スケジューラが実行されたときに実行されます。

ウェイ・ワン・スレッド2はすぐに実行されます。スレッド1がスレッド2に目を覚ますように伝えると(スレッド2が実際に何回も実行する前に)、条件変数で待機しているスレッドはすべて、スケジューラはそれらを実行します。あなたの例では、スレッド1が終了する前にスケジューラーがスレッド2になることは決してありません。あなたのタイムスライスは1msです。

スレッド2はすぐに実行されません。スレッド1はスレッド2を通知しますが、スレッド2が起動するとスレッド1はロックを保持します(スレッド1が後でループするか、スレッド2がすぐに覚醒し、スレッド1の前にmutexがロックされてcond_signalと呼ばれる)、スレッド2は現在ミューテックスのウェイターとしてスリープ状態に戻ります。

スレッド2が待機スレッドであるとします。

スレッド2をすぐに起動させると仮定すると、AFAIKには何もできません。

シグナル1の後にスレッド1をSTOPにしておきたい場合、pthread_condを使用したい場合は、シグナルを送る前にmutexのロックを解除してからsched_yield()またはnanosleep() {0,0})が信号の直後に表示されます。スレッド1がスレッドの行の(優先度が同じで)実行を待っているだけなので、パフォーマンスが悪くなります。これにより、コンテキストスイッチの実行時比率が高くなります。これは余分なロック解除/ロックのためにも悪いことです。このすべての上に、スレッド1はスレッド2の前に再び起きるかもしれません!あなたはループを使用しなければならないでしょう、シグナリングを続けて、そのスレッド2がその仕事をするまで降伏してください! Klunky!ところで

、揮発性の私の解釈では、変数はいつでも変更することが予想されるコンパイラに伝えることです。この変数は、パフォーマンス上の理由から、関数の開始時にレジスタにコピーするのに悪いものです。コンパイラは、どの変数がこれを行うことができるかを知ることについてはかなり良いですが、時には間違いを起こします。私は、最適化が有効になったときにvolatileがになると、gccがレジスタ内の共有変数をキャッシュすると、は効果があると言います。 gccがグローバルにレジスタにキャッシュするかどうかはわかりません。

歓声。

0

私はここ2つのミスを見ることができます:

  • あなたはpthread_cond_signalを周りミューテックスをロックされていません。これは、他のスレッドがpthread_cond_waitに進入するかどうかをチェックするかどうかのチェックの間にシグナルが送信されることを意味し、シグナルがここで失われる可能性があります。

  • 共有変数はvolatileでなければなりません。コンパイラは、ループ外のアクセスを移動するか、ループの一部を展開して、ループ本体の2つのインスタンス間のアクセスを省略することができます。

これらのどちらも、表示されている症状を直接説明するものではありません。しかし、与えられた振る舞いは絶対に規格の許容範囲内です。

+0

は、私が最初の間違いを理解していなかった - 最初のスレッドが最初のスレッドが信号を欠場することができますどのようにその状態をチェックしている間に、他のスレッドがクリティカルセクションにすることはできません?。行動が許容範囲内にあることはどういう意味ですか?すぐに起床してレディキューまたはロックキューを置くと考えられるcond.signalではありません。 – dasman

+0

'volatile'は正しいpthreadsコードには必要ありません。必要なsyncrhonisation関数(例えば、 'pthread_mutex_lock()')はコンパイラの障壁を意味します。 – caf

2

ご質問に直接お答えください:いいえ、pthread_mutex_unlockおよびpthread_cond_signalすぐに待っているスレッドを解除しないでください。代わりに、「実行準備が整った」とマークするだけで、OSはそれが気になるときに目覚めたスレッドをスケジュールします。もちろん、OSはそのスレッドに直ちに切り替えることを決定することができます(特に、現在実行中のスレッドより優先度が高い場合)。しかし、そうでない場合もあります。

しかし、あなたのコードは、とにかく書かれているとおりに正しく動作しない場合があります。両方のスレッドを同時に実行している可能性があります。

ちょうどpthread_cond_waitが返されているため、条件変数が通知されたことを意味するものではありません。これは「偽の目覚め」と呼ばれます。 pthread_cond_waitを正しく使用するには、ループ内に置く必要があります。このループでは、pthread_cond_waitを呼び出す直前に、ミューテックスが保持されている間に起床に関連する条件がテストされます。例えば

void wait_until_signalled(int* wake_flag,pthread_cond_t* cond,pthread_mutex_t* mutex) 
{ 
    pthread_mutex_lock(&mutex); 
    while(!(*wake_flag)) /* if the int pointed to by wake_flag is non-zero then wake up */ 
    { 
     pthread_cond_wait(&cond,&mutex); 
    } 
    pthread_mutex_unlock(&mutex); 
} 

void signal(int* wake_flag,pthread_cond_it* cond,pthread_mutex_t* mutex) 
{ 
    pthread_mutex_lock(&mutex); 
    *wake_flag=1; /* tell the waiting thread that it should wake */ 
    pthread_mutex_unlock(&mutex); 
    pthread_cond_signal(&cond); /* wake up the thread if it is blocked in pthread_cond_wait*/ 
} 

はもちろん、おそらくpthread_xxx呼び出しの戻り値をチェックしたいです。

によって指し示される値は、mutexをロックしてチェックして変更するだけなので、設定されていれば確実にウェイクし、フラグがセットされるまでwait_until_signalledから戻ることはありません。 pthread_cond_waitへの呼び出しはスレッドを待ち状態に原子的にマークし、ミューテックスのロックを解除するので、pthread_cond_signalへの呼び出しはスレッドが待機していることを確認し、(既に設定されている)フラグをチェックできるようにするか、これは、スレッドがmuttをロックした後でなければならないことを意味します。signalにフラグを設定します。この場合、待機中のスレッドはフラグセットを参照して戻ります。

関連する問題