ein Kapitel zurück                                           ein Kapitel weiter

Eine weitere Möglichkeit Threads zu steuern stellt ein sogenannter Monitor dar. Ein Monitor ist ein Modul das bestimmte Daten und Funktionen beinhaltet die "beschützt" werden müssen. Damit ist es möglich das höchstens ein Thread diese Monitorprozedur ausführt. Dies kennen wir ja bereits in ähnlicher Form von den Mutexen.

Das Problem daran ist aber, wenn ein Thread mal keine Arbeit hat, blockiert dieser solange alle anderen Threads. Dies ist reine Zeitverschwendung wenn andere Threads während dieser Zeit etwas sinnvolles erledigen könnten. Man spricht dabei von "race condition".

Zur Lösung diese Problems wurde sogenannte Condition-Variablen in die Bibliothek implementiert. Somit wartet ein Thread anstatt auf die Beendigung seiner Arbeit oder eines anderen Threads auf eine entsprechende Condition-Variable.

Dies können sie sich bildlich folgendermaßen Vorstellen.......




Ein Thread setz also den Mutex und überprüft ob er etwas zu tun hat. Fall nein wird der Mutex freigegeben und der Thread in eine Wartestellung gesetzt. Nicht gleichzusetzen mit Blockieren. Der Thread wartet nun solange bis Ihm ein anderer Thread die Condition-Variable sendet.

Um eine Condition-Variable zu verwenden müssen wir diese erst initialisieren........

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);  

Die Funktion zum Warten einer Condition-Variable eines anderem Theads sieht wie folgt aus...

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,
                          pthread_mutex_t *mutex,struct timespec *exp);  

Mit pthread_cond_timewait können sie im Gegensatz zu pthread_cond_wait ereichen, daß der Thread nicht beliebig nicht beliebig lange wartet, sondern nur eine gewisse Zeit. Da wir .._timewait nicht in diesem Buch verwenden werden hier schnell ein Snippet, wie man diese Funktion anwendet........



#include <sys/time.h> /*Mehr dazu unter man gettimeofday*/
.......
wait.tv_sec = 2;
wait.tv_nsec = 0;
......
pthread_cond_timewait(&cond, &mutex, &wait);
/*Thread wartet 2 Sekunden bis er weiterläuft*/  

Nun wartet unser Thread 2 Sekunden. Vorausgesetzt natürlich sie haben die Condition-Variable initialisiert und ebenso den Mutex.

Aufwecken bzw. die Condition Variable können sie mit den Funktionen.......

int pthread_cond_signal(pthread_cond_t *cond)
int pthread_cond_broadcast(pthread_cond_t *cond);  

Die Funktion pthread_cond_signal weckt dabei einen Wartenden Thread auf. Welcher das ist liegt leider nicht in Ihrer Hand. Mit der Funktion pthread_cond_broadcast werden alle Threads aufgeweckt die mit der Condition pthread_cond_wait und pthread_cond_timewait "schlafen" gelegt wurden bzw. auf die Condition Variable warten.

Beenden können sie eine Condition-Variable mit der Funktion..........

int pthread_cond_destroy(pthread_cond_t *cond);  

Nun wollen wir uns ein einfaches Beispiel dazu ansehen.............

/*Download:thread10.c*/
#include <stdio.h> #include <pthread.h> pthread_cond_t cond; pthread_mutex_t mutex; int werte[10]; /*pthread_cond_t cond = PTHREAD_COND_INITIALIZER;*/ void thread1(void *arg) { int ret,i; printf("Thread 1 : %ld gestartet.....\n",pthread_self()); sleep(1); ret=pthread_mutex_lock(&mutex); if(ret != 0) { printf("Fehler bei lock in Thread : %ld\n",pthread_self()); exit(0); } /*Kritischer Codeabschnitt*/ for(i=0;i<10;i++) werte[i]=i; printf("Thread 1 : %ld sendet das Signal für die Condition Variable\n"); pthread_cond_signal(&cond); ret=pthread_mutex_unlock(&mutex); if(ret != 0) { printf("Fehler bei unlock in Thread : %ld\n",pthread_self()); exit(0); } printf("Thread 1 : %ld beendet\n"); pthread_exit((void *)0); } void thread2(void *arg) { int ret,i; int summe=0; printf("Thread 2 : %ld wartet auf die Condition Variable\n",pthread_self()); pthread_cond_wait(&cond, &mutex); printf("Thread 2 : %ld gestartet.....\n",pthread_self()); for(i=0; i<10; i++) summe+=werte[i]; printf("Thread 2 : %ld beendet\n"); printf("Summe aller Zahlen beträgt : %d\n",summe); pthread_exit((void *)0); } int main() { pthread_t th[2]; pthread_cond_init(&cond,NULL); pthread_mutex_init(&mutex, NULL); pthread_create(&th[0],NULL, (void *)thread1, NULL); pthread_create(&th[1],NULL, (void *)thread2, NULL); pthread_join(th[0],NULL); pthread_join(th[1],NULL); return 0; }

In diesem Beispiel wartet unser Thread Nummer 2 auf die Condition Variable von Thread 1. Thread 1 weist dem globlen Zahlenarray werte 10 Werte zu die Thread 2 anschließend berrechnet.

Dies ist natürlich auch wieder ein primitives Beispiel und soll nur die Funktion von Condition Variablen demonstrieren. Im Beispiel oben habe ich.............

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  

...auskommentiert. Sie könnten anstatt der Funktion pthread_cond_intit die global Variable cond gleich mit der Konstante PTHREAD_COND_INITIALIZER definieren.

Wollen wir uns dazu wieder ein Beispiel anschauen. Wir simulieren ein Programm das Daten empfängt und erzeugen dabei 2 Threads. Jeder dieser beiden Threads wird mit pthread_cond_wait in einen Wartezustand geschickt und warten auf das Signal pthread_cond_signal vom Mainthread.

Dieser simuliert dann er würde 2 Datenpakete an einem Thread verschicken. Der Thread simuliert dann er würde die Datenpakete bearbeiten. Lassen sie einfach das Programm ablaufen. Ich denke es erklärt sich von selbst.

/*Download:thread11.c*/
#define _MULTI_THREADED #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void checkResults(string, val) { if (val) { printf("Failed with %d at %s", val, string); exit(1); } } #define NUMTHREADS 2 pthread_mutex_t dataMutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t dataPresentCondition = PTHREAD_COND_INITIALIZER; int dataPresent=0; int sharedData=0; void *theThread(void *parm) { int rc; int retries=2; printf("Consumer Thread %.8x : Entered\n", pthread_self()); rc = pthread_mutex_lock(&dataMutex); checkResults("pthread_mutex_lock()\n", rc); while (retries--) { while (!dataPresent) { printf("Consumer Thread %.8x : Warte auf Daten zum bearbeiten\n" ,pthread_self()); rc = pthread_cond_wait(&dataPresentCondition, &dataMutex); if (rc) { printf("Consumer Thread %.8x : condwait failed, rc=%d\n" ,rc,pthread_self()); pthread_mutex_unlock(&dataMutex); exit(1); } } printf("Consumer Thread %.8x : Daten wurden gemeldet, " " - Bearbeite die Daten sollange sie geschützt sind (lock)\n" ,pthread_self()); if (sharedData==0) {dataPresent=0;} }//Ende while printf("Consumer Thread %.8x : Alles erledigt\n",pthread_self()); rc = pthread_mutex_unlock(&dataMutex); checkResults("pthread_mutex_unlock()\n", rc); return NULL; } int main(int argc, char **argv) { pthread_t thread[NUMTHREADS]; int rc=0; int amountOfData=4; int i; printf("Enter Testcase - %s\n", argv[0]); printf("Create/start threads\n"); for (i=0; i <NUMTHREADS; ++i) { rc = pthread_create(&thread[i], NULL, theThread, NULL); checkResults("pthread_create()\n", rc); }//Ende for /* The producer loop */ while (amountOfData--) { printf("Producer: Daten gefunden\n"); sleep(3); rc = pthread_mutex_lock(&dataMutex); /* Protect shared data and flag */ checkResults("pthread_mutex_lock()\n", rc); printf("Producer: Sperre die Daten und gib eine Meldung an Consumer\n"); ++sharedData; /* Add data */ dataPresent=1; /* Set boolean predicate */ rc = pthread_cond_signal(&dataPresentCondition); /* wake up a consumer */ if (rc) { pthread_mutex_unlock(&dataMutex); printf("Producer: Failed to wake up consumer, rc=%d\n", rc); exit(1); } printf("Producer: Gib die gesperrten Daten und das Flag wieder frei\n"); rc = pthread_mutex_unlock(&dataMutex); checkResults("pthread_mutex_lock()\n",rc); } //Ende while printf("Wartet bis die Threads beendet sind und Ihre Resourcen freigibt\n"); for (i=0; i <NUMTHREADS; ++i) { rc = pthread_join(thread[i], NULL); checkResults("pthread_join()\n", rc); } printf("Clean up\n"); rc = pthread_mutex_destroy(&dataMutex); rc = pthread_cond_destroy(&dataPresentCondition); printf("Main completed\n"); return 0; }

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf