pthread_cond_signal provoquant un blocage

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:

  • Dans le programme principal, mutex et condvar sont des champs membres et la fonction est une méthode membre.
  • La tâche effectue une tâche au lieu de dormir.
  • Plusieurs threads peuvent attendre et nous devrions diffuser plutôt que de signaler. Cependant, l’interblocage est reproductible à 100% même lorsque j’utilise un signal et un fil en attente.

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.