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:
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")
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.
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.