5 Stimmen

Pthread bedingtes Signal - funktioniert nicht wie erwartet

Ich arbeite an einem Projekt und versuche, die pthread_cond_wait() et pthread_cond_signal() um zwei Threads zu synchronisieren.

Mein Code sieht in etwa so aus:

pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t write_it = PTHREAD_COND_INITIALIZER;

   int main(int argc, char**argv)
  {

     pthread_t t_send_segments, t_recv_acks;

     pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL);
     pthread_create(&t_recv_acks,     NULL, recv_acks,     (void*)NULL);

     pthread_join(t_recv_acks, (void**)NULL);

     pthread_mutex_destroy(&lock_it);
     pthread_cond_destroy(&write_it);
}

 void* send_segments(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("s1\n");
       printf("s2\n");
       pthread_cond_wait(&write_it, &lock_it);
       printf("s3\n");
       printf("s4\n");
       printf("s5\n"); 
       pthread_mutex_unlock(&lock_it);
    }
    return 0;
 }

 void* recv_acks(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("r1\n");
       pthread_cond_signal(&write_it);
       printf("r2\n");
       pthread_mutex_unlock(&lock_it);
    }  
    return 0;
 }

Die erwartete Ausgabe ist:

s1
s2
r1
s3
s4
s5
s1
s2
r2
r1
s3
s4
s5

(etc)

Meine Ausgabe folgt diesem Muster überhaupt nicht. Offensichtlich habe ich irgendwo einen logischen Fehler, aber ich verstehe nicht, wo. Warum funktioniert die recv_acks() Gewinde siempre Ausbeute, wenn sie auf die pthread_cond_signal() - da die pthread_cond_wait() wird immer zuerst ausgeführt (wegen der Reihenfolge, in der ich die Threads erstelle) und die cond_wait() immer ausgeführt wird, da es sich im kritischen Abschnitt befindet?

8voto

tarmin Punkte 116

El pthread_cond_signal Funktion führt nicht dazu, dass der aktuelle Thread aufgibt und gibt den Mutex nicht frei. Es wird lediglich ein Thread neu gestartet, der sich aufgrund der Bedingung über pthread_cond_wait . Dies bedeutet nur, dass der aufgeweckte Thread für die Planung zur Verfügung steht, es führt nicht dazu, dass er sofort ausgeführt wird. Der Thread-Scheduler wird ihn irgendwann in der Zukunft einplanen.

Und nur weil der s-Thread aufgeweckt wurde und sich um den Mutex bewirbt, heißt das nicht, dass er den Mutex als nächstes bekommt. Mutexe sind nicht unbedingt fair gegenüber allen Threads, die sie angefordert haben. Gemäß der pthread_mutex man-Seite: " pthread_mutex_lock sperrt den angegebenen Mutex. Wenn die Mutex gerade nicht gesperrt ist, wird sie gesperrt und gehört dem aufrufenden Thread, und pthread_mutex_lock kehrt sofort zurück." Der r-Thread kann sich also mehrmals in seiner Schleife drehen und den Mutex mehrmals entsperren und wieder sperren, bevor er vom Scheduler ausgelagert wird. Das bedeutet, dass der s-Thread nur dann eine Chance auf den Mutex hat, wenn der Scheduler den r-Thread während der kurzen Zeit, in der er den Mutex freigegeben hat, zufällig unterbricht.

Um die gewünschte Ausgabe zu erzielen, müssen beide Threads ihre Ausführung mit einer Bedingung steuern und sich gegenseitig signalisieren, bevor sie sich selbst anhalten. Dies ist jedoch nicht unbedingt das, was Sie in Ihrem realen Projekt tatsächlich tun wollen.

Ein weiterer Hinweis: Es spielt keine Rolle, in welcher Reihenfolge Sie die Themen erstellt haben. Das Erzeugen eines Threads führt nicht zum Erzeugen des Threads. Der Haupt-Thread wird also wahrscheinlich beide Threads erstellen, bevor einer von ihnen geplant wird, und der Thread-Scheduler kann einen der beiden als nächstes zur Ausführung planen. Wenn der s-Thread auf Ihrer Plattform zuerst ausgeführt wird, ist das nur zufällig das Implementierungsverhalten auf Ihrer Plattform und nichts, worauf Sie sich verlassen sollten.

6voto

David Z Punkte 121773

Ich meine mich zu erinnern, irgendwo gelesen zu haben, dass pthread_cond_signal() führt nicht dazu, dass der Thread sofort aufgibt. Da pthread_cond_signal() die Sperre nicht freigibt, muss der Thread, der sie aufruft, die Ausführung fortsetzen, bis die Sperre freigegeben wird, und nur dann gibt er nach und erlaubt dem signalisierten Thread die Rückkehr von pthread_cond_wait() .

Wenn das der Fall ist, dann würde Ihre Ausgabe wie folgt aussehen

s1
s2
r1
r2
s3
s4
s5

etc.... wenn das nicht der Fall ist, müssen Sie möglicherweise die tatsächliche Ausgabe in Ihre Frage einfügen, um eine genaue Antwort zu erhalten.

4voto

Ben Punkte 6884

Sie verwenden die Bedingungen falsch. Ich habe nicht genau verstanden, was Sie wollen, aber was Sie zu tun versuchen, sieht sehr nach einem typischen Synchronisationsproblem aus, dem Producer/Consumer-Problem, das mit Bedingungen implementiert werden kann, wie ich es nachher getan habe.

Sie sollten einen Blick darauf werfen, um zu verstehen, wie die Bedingungen verwendet werden. Das Problem ist das folgende: Ich habe zwei Threads, von denen einer Daten in einen Puffer mit fester Größe schreibt und der andere Daten aus dem Puffer liest. Der erste Thread kann keine weiteren Daten schreiben, wenn der Puffer voll ist, der zweite kann nicht lesen, wenn der Puffer leer ist.

#include <stdlib.h>
#include <stdio.h>

#include <pthread.h>

#define BUFFER_SIZE 4 

int buffer_nb_entries = 0; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

void send(){
for(;;){
  pthread_mutex_lock(&mutex);
  while( buffer_nb_entries == BUFFER_SIZE) /* If buffer is full, then wait */
    pthread_cond_wait(&cond, &mutex); 

  /* Here I am sure that buffer is not full */
  printf("sending\n"); 
  buffer_nb_entries++;

  pthread_cond_signal(&cond); // signal that the condition has changed. 
  pthread_mutex_unlock(&mutex); 
 }
}

void receive(){
  for(;;){
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == 0)
      pthread_cond_wait(&cond, &mutex);
    /* Here I am sure that buffer is not empty */
    printf("receiving\n");
    buffer_nb_entries--; 
    pthread_cond_signal(&cond); 
    pthread_mutex_unlock(&mutex); 
  }

}

int main(){
  pthread_t s, r; 

  pthread_create(&s, NULL, (void *(*)(void*))send, NULL); 
  pthread_create(&r, NULL, (void *(*)(void*))receive, NULL); 

  pthread_join(s, NULL); 

}

Beachten Sie, dass Sie auf etwas testen müssen, bevor Sie pthread_cond_wait() aufrufen. Wenn Sie das nicht tun und die Signalfunktion bereits aufgerufen wurde, können Sie für immer schlafen.

1voto

leif Punkte 2785

Ich glaube, Ihre Probleme rühren daher, dass Sie versuchen, ein Schloss für zu viele Dinge zu verwenden. Sie sollten eine Sperre nur für 1 Sache verwenden, so dass es keine Verwirrung darüber gibt, worauf Sie warten. Ich schlage vor, eine zweite Sperre für das Schreibsignal hinzuzufügen. Außerdem sollten Sie ein zweites cond_wait für die zweite Gruppierung von Nachrichten hinzufügen. Wenn Sie das nicht tun, wird die Reihenfolge, in der die Dinge ablaufen, zufällig sein. Hier ist meine bearbeitete Version Ihres Programms:

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

#define NUM_MESSAGES 3
int messages = 0;
void * send_segments(void *);
void * recv_acks(void *v);
pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;

   int main(int argc, char**argv)
  {

     pthread_t t_send_segments, t_recv_acks;

     pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL);
     pthread_create(&t_recv_acks,     NULL, recv_acks,     (void*)NULL);

     pthread_join(t_recv_acks, (void**)NULL);
     pthread_join(t_send_segments, (void**)NULL);

     //pthread_mutex_destroy(&lock_it);
     //pthread_cond_destroy(&write_it);
}

 void* send_segments(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("s1\n");
       printf("s2\n");
       pthread_mutex_unlock(&lock_it);

       // wait for other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_wait(&write_cond, &write_mutex);
       pthread_mutex_unlock(&write_mutex);

       pthread_mutex_lock(&lock_it);
       printf("s3\n");
       printf("s4\n");
       printf("s5\n"); 
       pthread_mutex_unlock(&lock_it);

       // wait for other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_wait(&write_cond, &write_mutex);
       pthread_mutex_unlock(&write_mutex);

       if (messages > NUM_MESSAGES) return( NULL );
    }
    return 0;
 }

 void* recv_acks(void *v) {
    for(;;) {
       // write first response
       pthread_mutex_lock(&lock_it);
       printf("r1\n");
       pthread_mutex_unlock(&lock_it);

       // signal other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_signal(&write_cond);
       pthread_mutex_unlock(&write_mutex);

       // write second response
       pthread_mutex_lock(&lock_it);
       printf("r2\n\n");
       // increment count before releasing lock, otherwise the other thread
       // will be stuck waiting for a write_cond signal
       messages++;
       pthread_mutex_unlock(&lock_it);

       // signal other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_signal(&write_cond);
       pthread_mutex_unlock(&write_mutex);

       if (messages > NUM_MESSAGES) return(NULL);
    } 
    return 0;
 }

Wenn ich dieses Programm ausführe, erhalte ich die folgende Ausgabe (beachten Sie, dass ich der Übersichtlichkeit halber einen zweiten Zeilenumbruch nach r2 hinzugefügt habe):

leif@indurain:~/tmp$ ./test
s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

leif@indurain:~/tmp$

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X