PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Threads in C..Synchronisation/Mutex


AlSvartr
2005-11-18, 14:24:59
Ich habe zu folgendem Code eine kleine Frage (ist mein erster Versuch mit Threads). Und zwar gehts mir darum, ob es denn ueberhaupt notwendig ist, die Threads auch noch "quasi-zusaetzlich" ueber die Konstanten bzw. currentOp zu synchronisieren, so dass auch nie was passieren kann, was nicht vorgesehen ist. Ich habs auch ohne diese Variante probiert und es funktionierte auch, aber dann stellte ich mir die Frage, ob es nicht doch passieren kann, dass der zweite Thread quasi zu frueh den Mutex lockt und konvertiert, ohne dass der erste eingelesen hat oder aehnliches. Oder ist das ausgeschlossen?

Zur Erklaerung: Ist ne kleine Uebungsaufgabe gewesen, der erste Thread soll ne Zeichenkette auslesen, der zweite Thread soll sie in Großbuchstaben konvertieren und ausgeben. Also fange ich an, indem ich mit dem ersten Thread den Mutex locke, in input einlese, den Mutex unlocke, im zweiten Thread locke, konvertiere, waehrend der erste Thread dann wartet, bis der Mutex wieder frei ist. Nach der Konvertierung erfolgt dann der Unlock seitens des zweiten Threads, womit der erste wieder lockt, die Ausgabe macht und dann wieder ne neue Zeichenkette einliest (trylock hab ich deswegen verwendet, weil ich so eben auf das unlocken zum Ende des ersten Threads verzichten kann..wenn ich da allerdings unlocken wuerde, wuerde ja wieder der zweite Thread loslegen, was die "zusaetzliche" Synchronisierung ueber mein currentOp etwas sinnvoller erscheinen laesst. Ratschlaege erwuenscht ;)

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;
char input[31];
int currentOp;
const int READ_STRING=1;
const int CONVERT_STRING=2;
const int PRINT_STRING=3;

void *readinput () {
do {
pthread_mutex_lock(&mutex); /* Trylock, weil sonst Block ab zweitem Durchlauf */
if (currentOp==READ_STRING) {
printf("Enter String: ");
fgets(input,31,stdin);
currentOp=CONVERT_STRING;
}
pthread_mutex_unlock(&mutex); /* Unlock -> Konvertierung kann in zweitem Thread erfolgen */
pthread_mutex_lock(&mutex); /* Lock fuer Ausgabe und erneutes Einlesen */
if (currentOp==PRINT_STRING) {
printf("%s\n",input);
currentOp=READ_STRING;
}
pthread_mutex_unlock(&mutex);
} while (1);
}

void *convert () {
do {
pthread_mutex_lock(&mutex); /* Lock fuer Konvertierung */
if (currentOp==CONVERT_STRING) {
int i;
for (i=0;i<strlen(input);i++) {
if (islower(input[i]))
input[i]=toupper(input[i]);
printf("%d Zeichen konvertiert\r",i+1);
}
printf("\n");
for (i=0;i<5;i++) {
printf("Warte 5 Sekunden: %d\r",i);
fflush(stdout);
sleep(1);
}
printf("Warte 5 Sekunden: 5\n");
currentOp=PRINT_STRING;
}
pthread_mutex_unlock(&mutex); /* Unlock nach Konvertierung und sleep */
} while (1);
}

int main () {
currentOp=1;

pthread_t p1,p2;

pthread_mutex_init(&mutex,NULL);

pthread_create(&p1,NULL,readinput,NULL);
pthread_create(&p2,NULL,convert,NULL);

pthread_join(p1,NULL);
pthread_join(p2,NULL);

printf("\n");
return 0;
}

Trap
2005-11-18, 18:11:20
Dein Programm ist ziemlich ungeschickt implementiert. Es kann auch ein Thread die Mutex locken wenn er garnicht an der Reihe ist und theoretisch kann das beliebig lang/oft passieren.

AlSvartr
2005-11-18, 18:15:48
Ein Hinweis, wie's geschickter geht, waer nett :)

Trap
2005-11-18, 18:35:37
currenOp prüfen bevor man die Mutex locked, nicht danach. Ändern darf man currentOp natürlich nur in der Mutex, aber lesen ist von überall ok (bei atomaren Datentypen).

while(currentOp!=READ_STRING) ; //<- das Semikolon ist Absicht
pthread_mutex_lock(&mutex);

AlSvartr
2005-11-18, 18:52:09
Dann haette ich aber doch aktives Warten, das is ja auch nich so sexy..hm..mit pthread_cond_wait und pthread_cond_signal ließe sich das vermutlich machen, oder?

<edit>
So stell ich mir das fuer die beiden Threads etwa vor:

void *readinput () {
do {
printf("Enter String: ");
fgets(input,31,stdin);

pthread_cond_signal(&cond);

pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
printf("%s\n",input);
pthread_mutex_unlock(&mutex);
} while (1);
}

void *convert () {
do {
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
int i;
for (i=0;i<strlen(input);i++) {
if (islower(input[i]))
input[i]=toupper(input[i]);
printf("%d Zeichen konvertiert\r",i);
}
printf("\n");
for (i=0;i<5;i++) {
printf("Warte 5 Sekunden: %d\r",i);
fflush(stdout);
sleep(1);
}
printf("Warte 5 Sekunden: 5\n");
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
} while (1);
}


<edit> Hm..ich ueberlege gerade...setzt denn pthread_cond_wait die Mutex wenn die Condition erfuellt ist? Erstmal ausprobieren ;)

<edit2> Also, grad mal probiert, wenn ich das richtig sehe, lockt pthread_cond_wait die Mutex nach Erfuellung der Condition und der Thread laeuft dann weiter..die obige Loesung funktioniert demnach auch (auch getestet) - aber ist das nun so sinnvoll/in Ordnung? ;)

Trap
2005-11-18, 19:20:01
Ok, mal ein Versuch mit 3 Mutex:

mutex mut,full,empty;
lock(full);

read() {
lock(empty)
lock(mut)
//dostuff
unlock(mut)
unlock(full)
}

convert() {
lock(full)
lock(mut)
//dostuff
unlock(mut)
unlock(empty)
}

Ich glaub es ging auch ohne die mutex mut, aber ich bin mir grad nicht sicher...