pthreads
Variables conditionnelles
Recherche…
Introduction
Les variables conditionnelles sont utiles dans les cas où vous souhaitez qu'un thread attend quelque chose qui se produit dans un autre thread. Par exemple, dans un scénario producteur / consommateur avec un ou plusieurs threads producteurs et un seul thread consommant, les variables conditionnelles peuvent être utilisées pour signaler le thread consommant que de nouvelles données sont disponibles.
Remarques
Processus général
Une attente sur une variable conditionnelle (queueCond dans l'exemple producteur / consommateur) est toujours couplée à un mutex (le queueMutex dans l'exemple producteur / consommateur) et doit toujours être couplée à une variable d'état "normale" (queue.empty ( ) dans l'exemple producteur / consommateur). Utilisé correctement, cela garantit qu'aucune nouvelle donnée n'est manquée chez le consommateur.
En général, le processus devrait être:
- Pour le fil de signalisation:
- Verrouille le mutex
- Mettre à jour les données et les variables d'état
- Signalez la variable de condition
- Débloquer le mutex
- Pour le thread en attente:
- Verrouille le mutex
- Faites une
whileboucle sur la variable d'état, en boucle tant que les données ne sont pas prêts - Dans le
whileen boucle, faire une attente sur la variable d'état avecpthread_cond_wait() - Lorsque le
whileen sortie de la boucle, nous sommes maintenant sûr que les nouvelles données sont prêtes, et que le mutex est verrouillé - Faire quelque chose avec les données
- Débloquer le mutex et répéter
Avec ce schéma, peu importe quand les threads de signalisation et d’attente sont programmés, le thread en attente ne manquera jamais de données (comme dans, il ne sera jamais bloqué en attente pour toujours avec des données valides prêtes). Cela peut être réalisé en essayant manuellement de parcourir les étapes pour le thread de signalisation, en enregistrant les états des variables mutex, condition et state, pour chacune des étapes du thread en attente.
pthread_cond_wait et le mutex
Pour faciliter le processus ci-dessus, il est nécessaire d'appeler pthread_cond_wait() avec le mutex verrouillé. Lorsqu'il est appelé, pthread_cond_wait() déverrouillera alors le mutex avant de mettre le thread en veille, et, juste avant de revenir pour quelque raison que ce soit, le mutex sera libéré. Cela signifie également que si un autre thread a actuellement le mutex verrouillé, pthread_cond_wait() attendra que le mutex soit déverrouillé, et jusqu'à ce que le thread en attente puisse réellement acquérir le mutex - il se heurtera à tout autre thread essayant de se verrouiller le mutex en même temps.
Réveils faux
, Il peut sembler aussi comme si while boucle d' attente sur la variable d'état pourrait être remplacé par un simple , if la déclaration. Cependant, la while en boucle est nécessaire, car la norme Posix permet pthread_cond_wait() de le faire soi-disant « parasites » redémarrages pendant l'attente, sans être réellement signalé. Ainsi, le code doit revérifier la variable d'état pour voir si pthread_cond_wait() est renvoyé en raison de la signalisation effective ou de l'un de ces réveils intempestifs.
Exemple producteur / consommateur
pthread_mutex_t queueMutex;
pthread_cond_t queueCond;
Queue queue;
void Initialize() {
//Initialize the mutex and the condition variable
pthread_mutex_init(&queueMutex, NULL);
pthread_cond_init(&queueCond, NULL);
}
void Producer() {
//First we get some new data
Data *newData = MakeNewData();
//Lock the queue mutex to make sure that adding data to the queue happens correctly
pthread_mutex_lock(&queueMutex);
//Push new data to the queue
queue.push(newData);
//Signal the condition variable that new data is available in the queue
pthread_cond_signal(&queueCond);
//Done, unlock the mutex
pthread_mutex_unlock(&queueMutex);
}
void Consumer() {
//Run the consumer loop
while(1) {
//Start by locking the queue mutex
pthread_mutex_lock(&queueMutex);
//As long as the queue is empty,
while(queue.empty()) {
// - wait for the condition variable to be signalled
//Note: This call unlocks the mutex when called and
//relocks it before returning!
pthread_cond_wait(&queueCond, &queueMutex);
}
//As we returned from the call, there must be new data in the queue - get it,
Data *newData = queue.front();
// - and remove it from the queue
queue.pop();
//Now unlock the mutex
pthread_mutex_unlock(&queueMutex);
// - and process the new data
ProcessData(newData);
}
}