web-dev-qa-db-ja.com

2スレッドの場合はpthread_cond_wait

2つのスレッドにpthread_cond_waitを実装しようとしています。私のテストコードは、2つのスレッドを使用して次のシナリオを実行しようとしています。

  • スレッドBは条件を待機します
  • スレッドAは「こんにちは」を5回印刷します
  • スレッドAはスレッドBに信号を送ります
  • スレッドAが待機します
  • スレッドBは「さようなら」を印刷します
  • スレッドBはスレッドAに信号を送ります
  • 開始するループ(x5)

これまでのところ、コードは「Hello」を5回出力してから、スタックします。私が見た例から、私は正しい方向に進んでいるようです。「ミューテックスをロックし、待って、他のスレッドからシグナルを受け取り、ミューテックスのロックを解除し、何かをし、ループする」

テストコード:

//Import 
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

//global variables
pthread_cond_t      condA  = PTHREAD_COND_INITIALIZER;
pthread_cond_t      condB  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;




void *threadA()
{
    int i = 0, rValue, loopNum;

    while(i<5)
    {
        //unlock mutex
        rValue = pthread_mutex_unlock(&mutex);

        //do stuff
        for(loopNum = 1; loopNum <= 5; loopNum++)
            printf("Hello %d\n", loopNum);

        //signal condition of thread b
        rValue = pthread_cond_signal(&condB);

        //lock mutex
        rValue = pthread_mutex_lock(&mutex);

        //wait for turn
        while( pthread_cond_wait(&condA, &mutex) != 0 )

        i++;
    }

}



void *threadB()
{
    int n = 0, rValue;

    while(n<5)
    {
        //lock mutex
        rValue = pthread_mutex_lock(&mutex);

        //wait for turn
        while( pthread_cond_wait(&condB, &mutex) != 0 )

        //unlock mutex
        rValue = pthread_mutex_unlock(&mutex);

        //do stuff
        printf("Goodbye");

        //signal condition a
        rValue = pthread_cond_signal(&condA);

        n++;        
    }
}




int main(int argc, char *argv[])
{
    //create our threads
    pthread_t a, b;

    pthread_create(&a, NULL, threadA, NULL);
    pthread_create(&b, NULL, threadB, NULL);

    pthread_join(a, NULL);
    pthread_join(b,NULL);
}

正しい方向へのポインタをいただければ幸いです。 (「gcctimeTest.c -o timeTest-lpthread」を使用してLinuxでコンパイルされたコード)

9
eddie-ryan

2つの問題があります。 1つ目は、while()ループを正しく使用していないことです。たとえば、次のようになります。

_//wait for turn
while( pthread_cond_wait(&condA, &mutex) != 0 )

i++;
_

whileループの本体は、ステートメント_i++_です。これにより、pthread_cond_wait()がエラーを返すまで、pthread_cond_wait()と_i++_が実行されます。これは本質的に無限ループです。

2つ目は、pthreads条件変数を単独で使用することはできないということです。実際の共有状態とペアにする必要があります(最も単純な場合、この共有状態は、ミューテックスによって保護されたフラグ変数である可能性があります)。 pthread_cond_wait()関数は、共有状態が特定の値に達するのを待つために使用され、pthread_cond_signal()関数は、スレッドが共有状態を変更したときに使用されます。このような変数を使用するように例を作り直します。

_//global variables
/* STATE_A = THREAD A runs next, STATE_B = THREAD B runs next */
enum { STATE_A, STATE_B } state = STATE_A;
pthread_cond_t      condA  = PTHREAD_COND_INITIALIZER;
pthread_cond_t      condB  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;

void *threadA()
{
    int i = 0, rValue, loopNum;

    while(i<5)
    {
        /* Wait for state A */
        pthread_mutex_lock(&mutex);
        while (state != STATE_A)
            pthread_cond_wait(&condA, &mutex);
        pthread_mutex_unlock(&mutex);

        //do stuff
        for(loopNum = 1; loopNum <= 5; loopNum++)
            printf("Hello %d\n", loopNum);

        /* Set state to B and wake up thread B */
        pthread_mutex_lock(&mutex);
        state = STATE_B;
        pthread_cond_signal(&condB);
        pthread_mutex_unlock(&mutex);

        i++;
    }

    return 0;
}

void *threadB()
{
    int n = 0, rValue;

    while(n<5)
    {
        /* Wait for state B */
        pthread_mutex_lock(&mutex);
        while (state != STATE_B)
            pthread_cond_wait(&condB, &mutex);
        pthread_mutex_unlock(&mutex);

        //do stuff
        printf("Goodbye\n");

        /* Set state to A and wake up thread A */
        pthread_mutex_lock(&mutex);
        state = STATE_A;
        pthread_cond_signal(&condA);
        pthread_mutex_unlock(&mutex);

        n++;
    }

    return 0;
}
_

ここでは、2つの条件変数condAcondBを使用する必要がないことに注意してください。代わりに、1つの条件変数のみを使用した場合、コードは同じように正しくなります。

30
caf

コードは実際に機能しますほぼ whileループに中括弧を追加すると、私のマシンでは問題ありません。

Cafが言ったことに加えて、threadAがすでにcondB信号を送信した後、threadBが開始されると無限ループに入ります。したがって、whileループで共有状態を使用する必要があるのはなぜですか。

47行目のusleep(1)を使用して人為的な遅延を導入し、自分の目で確かめることができます。

0
d9ngle