J’ai un programme qui bloque lorsque l’un des threads appelle pthread_cond_siganl
(ou broadcast). Le problème est reproductible à 100% dans le programme principal. Je ne pouvais pas comprendre ce qui n’allait pas avec elle et donc extrait le morceau de code appelant attente et signal. Cependant, l’impasse ne peut pas être reproduite avec le problème extrait.
L’exécution de valgrind
sur le programme principal ne signale aucune lecture / écriture non valide ni aucune fuite de mémoire.
Je veux savoir quelles sont les raisons possibles d’un blocage dans l’appel de pthread_cond_signal
.
L’extrait extrait suit.
#include #include #include #include #include #include using namespace std; void Task() { cerr << syscall(SYS_gettid) << " In Task, sleeping..." << endl; sleep(5); } pthread_mutex_t lock; pthread_cond_t cond; bool doingTheTask= false; void* func(void* ) { pthread_mutex_lock(&lock); if (doingTheTask) { cerr << syscall(SYS_gettid) << " wait... " << endl; while ( doingTheTask) {//spurious wake-up cerr << syscall(SYS_gettid) << " waiting..." << endl ; pthread_cond_wait(&cond, &lock); cerr << syscall(SYS_gettid) << " woke up!!!" << endl ; } } else { cerr << syscall(SYS_gettid) << " My Turn to do the task..." << endl; assert( ! doingTheTask ); doingTheTask= true; pthread_mutex_unlock(&lock); Task(); cerr << syscall(SYS_gettid) << " Before trying to acquire lock" << endl; pthread_mutex_lock(&lock); cerr << syscall(SYS_gettid) << " After acquiring lock" << endl ; assert( doingTheTask ); doingTheTask = false; cerr << syscall(SYS_gettid) << " Before broadcast" << endl; pthread_cond_broadcast(&cond); cerr << syscall(SYS_gettid) << " After broadcast" << endl; } pthread_mutex_unlock(&lock); return NULL; } int main() { pthread_mutex_init(&lock,NULL); pthread_cond_init(&cond,NULL); pthread_t thread[2]; for ( int i = 0 ; i < 2 ; i ++ ) { if (0 != pthread_create(&thread[i], NULL, func, NULL) ) { cerr << syscall(SYS_gettid) << " Error creating thread" << endl; exit(1); } } for ( int i = 0 ; i < 2 ; i ++ ) { pthread_join(thread[i],NULL); } pthread_mutex_destroy(&lock); pthread_cond_destroy(&cond); return 0; }
La seule fonction importante est la fonction func. Les autres parties sont juste présentées pour la compilation.
Comme je l’ai dit le problème n’est pas reproductible dans ce programme . La différence entre cet extrait et le programme principal est la suivante:
mutex
et condvar
sont des champs membres et la fonction est une méthode membre. Le problème que j’essaie de résoudre avec ce morceau de code est un mécanisme permettant d’exécuter la tâche une fois lorsqu’au moins l’un des threads nécessite sa réalisation. Mais deux threads ne devraient pas effectuer la tâche en parallèle et une fois que l’un d’eux l’a fait, les autres n’ont pas besoin de le faire. Les clients de cette méthode supposent qu’il bloque jusqu’à ce que la tâche soit terminée (donc, je ne peux pas revenir immédiatement après avoir vu que quelqu’un est en train de l’exécuter).
La trace des threads bloqués est la suivante:
#0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136 #1 0x00007ffff73e291c in pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:259
et
#0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136 #1 0x00007ffff73e30b1 in pthread_cond_signal@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_signal.S:142
Les blocages de pthread_cond_signal sont un problème similaire. Mais il semble que la personne qui pose la question avait une corruption de mémoire. Je n’ai pas de corruption de mémoire (dit valgrind
).
Le problème est reproductible à 100% sur les deux machines sur lesquelles je l’ai testé. (ArchLinux dernière et Uubntu 10.04.3).
Voici un exemple de sortie du programme principal. Cela montre à nouveau que les threads se bloquent avant d’appeler pthread_cond_wait
et pthread_cond_signal
. (La première colonne indique les identifiants de thread).
3967 In Task, sleeping... 3967 My Turn to do the task... 3967 In Task, sleeping... 3973 wait... 3973 waiting... 3976 3967 Before trying to acquire lock 3967 After acquiring lock 3967 Before broadcast
Le programme principal est en C ++. Mais j’utilise les parties C du langage et j’ai donc évité d’utiliser une balise C ++.
Erreur stupide. Je détruisais le mutex
et le condvar
avant d’exécuter le signal et d’attendre. Pour reproduire, il suffit de déplacer les fonctions de destruction avant de joindre les threads dans la fonction principale.
Il est toujours surprenant que sur mes deux machines, cela produise un comportement 100% cohérent (et faux).
Lorsque nous appelons pthread_cond_wait (& cond, & lock), le verrou est libéré et pthread attend la variable de condition. Quand il reçoit le signal sur la variable conditionnelle, il obtiendra le verrou et sortira de pthread_cond_wait (). Dans votre programme, vous avez acquis un verrou mutex avant d’appeler pthread_cond_broadcast (& cond). Par conséquent, pthread_cond_wait (& cond, & lock) ne peut pas utiliser le verrou lorsqu’il reçoit le signal. Je pense que ce sera la raison de l’impasse.