web-dev-qa-db-fra.com

Pourquoi une boucle while est-elle nécessaire autour des conditions d'attente pthread?

J'apprends les conditions pthread et d'attente. Pour autant que je sache, un fil d'attente typique est comme ceci:

pthread_mutex_lock(&m);
while(!condition)
     pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

Ce que je ne comprends pas, c'est pourquoi la ligne while(!condition) est nécessaire même si j'utilise pthread_cond_signal() pour réveiller le thread.

Je peux comprendre que si j'utilise pthread_cond_broadcast() je dois tester la condition, car je me réveille tous les threads en attente et l'un d'eux peut rendre la condition fausse à nouveau avant de déverrouiller le mutex (et transférant ainsi l'exécution à un autre thread réveillé qui ne devrait pas s'exécuter à ce stade). Mais si j'utilise pthread_cond_signal() je me réveille juste n thread donc la condition doit être vraie. Le code pourrait donc ressembler à ceci:

pthread_mutex_lock(&m);
pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

J'ai lu quelque chose sur les signaux parasites qui peuvent se produire. Est-ce (et seulement cela) la raison? Pourquoi devrais-je avoir des singals faux? Ou il y a autre chose que je ne comprends pas?

Je suppose que le code du signal est comme ceci:

pthread_mutex_lock(&m);
condition = true;
pthread_cond_signal(&cond); // Should wake up *one* thread
pthread_mutex_unlock(&m);
50
Emiliano

La vraie raison pour laquelle vous devez mettre pthread_cond_wait dans une boucle while n'est pas à cause d'un réveil parasite. Même si votre variable de condition n'avait pas de réveil parasite, vous auriez toujours besoin de la boucle pour détecter un type d'erreur courant. Pourquoi? Considérez ce qui peut arriver si plusieurs threads attendent dans la même condition:

Thread 1                         Thread 2           Thread 3
check condition (fails)
(in cond_wait) unlock mutex
(in cond_wait) wait
                                 lock mutex
                                 set condition
                                 signal condvar
                                 unlock mutex
                                                    lock mutex
                                                    check condition (succeeds)
                                                    do stuff
                                                    unset condition
                                                    unlock mutex
(in cond_wait) wake up
(in cond_wait) lock mutex
<thread is awake, but condition
is unset>

Le problème ici est que le thread doit libérer le mutex avant d'attendre, permettant potentiellement à un autre thread de "voler" tout ce que ce thread attendait. À moins qu'il ne soit garanti qu'un seul thread peut attendre à cette condition, il est incorrect de supposer que la condition est valide lorsqu'un thread se réveille.

47
Chris Lu

Supposons que vous ne vérifiez pas la condition. Ensuite, vous ne pouvez généralement pas éviter la mauvaise chose suivante (au moins, vous ne pouvez pas l'éviter dans une seule ligne de code):

 Sender                             Receiver
locks mutex
sets condition
signals condvar, but nothing 
  is waiting so has no effect
releases mutex
                                    locks mutex
                                    waits. Forever.

Bien sûr, votre deuxième exemple de code pourrait éviter cela en faisant:

pthread_mutex_lock(&m);
if (!condition) pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

Il serait alors certainement vrai que s'il n'y a qu'un seul récepteur au plus, et si cond_signal était la seule chose qui pouvait le réveiller, alors il ne se réveillait que lorsque la condition était définie et n'avait donc pas besoin d'une boucle. nos explique pourquoi le deuxième "si" n'est pas vrai.

16
Steve Jessop