pthreads
Bedingte Variablen
Suche…
Einführung
Bedingte Variablen sind nützlich, wenn ein Thread auf etwas warten soll, das in einem anderen Thread passiert. In einem Producer / Consumer-Szenario mit einem oder mehreren produzierenden Threads und einem verbrauchenden Thread können beispielsweise bedingte Variablen verwendet werden, um dem verbrauchenden Thread mitzuteilen, dass neue Daten verfügbar sind.
Bemerkungen
Allgemeiner Prozess
Ein Warten auf eine bedingte Variable (queueCond im Producer / Consumer-Beispiel) ist immer an einen Mutex (der queueMutex im Producer / Consumer-Beispiel) gekoppelt und sollte auch immer an eine "normale" Zustandsvariable gekoppelt sein (queue.empty ( ) im Hersteller / Verbraucher-Beispiel). Bei korrekter Verwendung wird dadurch sichergestellt, dass beim Verbraucher keine neuen Daten verfehlt werden.
Im Allgemeinen sollte der Prozess sein:
- Für den Signalisierungs-Thread:
- Sperren Sie den Mutex
- Aktualisieren Sie alle Daten und Statusvariablen
- Signalisieren Sie die Zustandsvariable
- Schalte den Mutex frei
- Für den wartenden Thread:
- Sperren Sie den Mutex
- Führen Sie eine
whileSchleife für die Zustandsvariable durch, solange die Daten noch nicht bereit sind - Warten Sie in der
whileSchleife mitpthread_cond_wait()auf die Bedingungsvariable. - Wenn die
whileSchleife beendet ist, sind wir jetzt sicher, dass neue Daten bereit sind und dass der Mutex gesperrt ist - Tun Sie etwas mit den Daten
- Entriegeln Sie den Mutex und wiederholen Sie den Vorgang
Bei diesem Schema wird der wartende Thread, unabhängig davon, wann der Signalisierungs- und der wartende Thread geplant sind, keine Daten übersehen (wie in, er wird niemals für immer mit gültigen Daten bereitstehen). Dies kann durch manuelles Durchlaufen der Schritte für den Signalisierungsthread erreicht werden, wobei die Zustände der Mutex-, Bedingungs- und Zustandsvariablen für jeden der Schritte im wartenden Thread aufgezeichnet werden.
pthread_cond_wait und der Mutex
Um den oben genannten Prozess zu vereinfachen, muss pthread_cond_wait() mit gesperrtem Mutex pthread_cond_wait() . Nach dem Aufruf wird pthread_cond_wait() den Mutex entsperren, bevor der Thread in den Ruhemodus versetzt wird. Kurz bevor er aus irgendeinem Grund zurückkehrt, wird der Mutex wieder gesperrt. Dies bedeutet auch, dass, wenn für einen anderen Thread derzeit der Mutex gesperrt ist, pthread_cond_wait() wartet, dass der Mutex pthread_cond_wait() wird, und bis der wartende Thread den Mutex tatsächlich erhalten kann der Mutex zur gleichen Zeit.
Wahnsinniges Aufwachen
Es kann auch so aussehen, als könnte die while Schleife, die auf die Zustandsvariable wartet, durch eine einfache if Anweisung ersetzt werden. Die while Schleife ist jedoch erforderlich, da der Posix-Standard es pthread_cond_wait() ermöglicht, während des Wartens ein sogenanntes "unechtes" Aufwachen durchzuführen, ohne dass dies tatsächlich signalisiert wird. Daher muss der Code die Zustandsvariable erneut prüfen, um zu sehen, ob pthread_cond_wait() zurückgegeben wird, weil sie tatsächlich signalisiert wurde oder aufgrund einer dieser falschen Wakeups.
Hersteller / Verbraucher Beispiel
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);
}
}