web-dev-qa-db-fra.com

pthread_cond_wait pour 2 threads

J'essaie d'implémenter pthread_cond_wait pour 2 threads. Mon code de test essaie d'utiliser deux threads pour réaliser le scénario suivant:

  • Le fil B attend la condition
  • Fil A imprime "Bonjour" cinq fois
  • Le fil A signale le fil B
  • Le fil A attend
  • Fil B imprime "Au revoir"
  • Le fil B signale le fil A
  • Boucle pour commencer (x5)

Jusqu'à présent, le code affiche "Bonjour" cinq fois et reste bloqué. D'après des exemples que j'ai consultés, il semble que je suis sur la bonne voie: "Verrouillez un mutex, attendez, soyez signalé par un autre thread, déverrouillez un mutex, faites des choses, bouclez"

Code de test:

//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);
}

Un pointeur dans la bonne direction serait grandement apprécié, merci! (Code compilé sous Linux en utilisant "gcc timeTest.c -o timeTest -lpthread")

9
eddie-ryan

Vous avez deux problèmes. La première est que vous n'utilisez pas correctement les boucles while() - par exemple, ici:

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

i++;

Le corps de la boucle while est l’instruction i++ - elle exécutera pthread_cond_wait() et i++ jusqu’à ce que la fonction pthread_cond_wait() renvoie une erreur. Il s’agit donc essentiellement d’une boucle sans fin.

La seconde est que vous ne pouvez pas utiliser une variable de condition pthreads elle-même - elle doit être associée à un état partagé réel (au plus simple, cet état partagé pourrait simplement être une variable drapeau protégée par un mutex). La fonction pthread_cond_wait() est utilisée pour attendre que l'état partagé atteigne une certaine valeur, et la fonction pthread_cond_signal() est utilisée lorsqu'un thread a modifié l'état partagé. Reprendre votre exemple pour utiliser une telle variable:

//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;
}

Notez que l'utilisation de deux variables de condition condA et condB n'est pas nécessaire ici - le code serait tout aussi correct si une seule variable de condition était utilisée à la place.

30
caf

Le code fonctionne réellement presque bien sur ma machine lorsque vous ajoutez des accolades à la boucle while.

En ajoutant à ce que caf a dit, vous entrerez dans une boucle infinie lorsque threadB sera démarré après que threadA ait déjà envoyé le signal condB, d'où la nécessité d'utiliser un état partagé dans votre boucle while.

Vous pouvez introduire un délai artificiel en utilisant usleep(1) à la ligne 47 et voyez par vous-même.

0
d9ngle