2012-12-02 10 views
9

Próbuję wdrożyć pthread_cond_wait dla 2 wątków. Mój kod testowy próbuje użyć dwóch wątków preform następujący scenariusz:pthread_cond_wait na 2 wątki

  • Wątek B czeka na stan
  • drukuje
  • wątku „Hello” pięciokrotnie
  • Gwint Gwint sygnały B
  • nawlec czeka
  • wydruki gwintu B "widzenia"
  • sygnały B
  • wątku nawlec
  • pętli start (x5)

Do tej pory kod drukuje "Hello" pięć razy, a następnie utknie. Z przykładów Sprawdziliśmy wydaje się, że jestem na dobrej drodze, „Blokada mutex, czekaj, zamów sygnalizowane przez inny wątek, odblokować mutex, robić rzeczy, pętlę”

Code Test:

//Import 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <unistd.h> 

//global variables 
pthread_cond_t  condA = PTHREAD_COND_INITIALIZER; 
pthread_cond_t  condB = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; 




void *threadA() 
{ 
    int i = 0, rValue, loopNum; 

    while(i<5) 
    { 
     //unlock mutex 
     rValue = pthread_mutex_unlock(&mutex); 

     //do stuff 
     for(loopNum = 1; loopNum <= 5; loopNum++) 
      printf("Hello %d\n", loopNum); 

     //signal condition of thread b 
     rValue = pthread_cond_signal(&condB); 

     //lock mutex 
     rValue = pthread_mutex_lock(&mutex); 

     //wait for turn 
     while(pthread_cond_wait(&condA, &mutex) != 0) 

     i++; 
    } 

} 



void *threadB() 
{ 
    int n = 0, rValue; 

    while(n<5) 
    { 
     //lock mutex 
     rValue = pthread_mutex_lock(&mutex); 

     //wait for turn 
     while(pthread_cond_wait(&condB, &mutex) != 0) 

     //unlock mutex 
     rValue = pthread_mutex_unlock(&mutex); 

     //do stuff 
     printf("Goodbye"); 

     //signal condition a 
     rValue = pthread_cond_signal(&condA); 

     n++;   
    } 
} 




int main(int argc, char *argv[]) 
{ 
    //create our threads 
    pthread_t a, b; 

    pthread_create(&a, NULL, threadA, NULL); 
    pthread_create(&b, NULL, threadB, NULL); 

    pthread_join(a, NULL); 
    pthread_join(b,NULL); 
} 

Wskazówka w dobrym kierunku byłaby bardzo ceniona, dzięki! (Kod skompilowany w systemie Linux za pomocą "gcc timeTest.c -o timeTest -lpthread")

+0

Nie jest to wymagane, głównie próbowałem wariacji, ale jak już powiedziałeś, idealnie byłoby użyć tylko jednego –

Odpowiedz

28

Masz dwa problemy. Pierwszym jest to, że nie używasz while() pętle prawidłowo - na przykład tutaj:

//wait for turn 
while(pthread_cond_wait(&condA, &mutex) != 0) 

i++; 

Ciało pętli while jest oświadczenie i++ - to wykona pthread_cond_wait() i i++pthread_cond_wait() zwraca błąd, tak jest to w istocie nieskończona pętla.

Po drugie, nie można samodzielnie użyć zmiennej warunkowej pthreads - musi ona być sparowana z pewnym rzeczywistym stanem współdzielonym (w najprostszym przypadku ten współużytkowany stan może być po prostu zmienną flagową chronioną przez muteks) . Funkcja pthread_cond_wait() służy do czekania, aż stan współdzielony osiągnie określoną wartość, a funkcja pthread_cond_signal() jest używana, gdy wątek zmienił stan współdzielony. Przerabianiu swój przykład użyć takiej zmiennej:

//global variables 
/* STATE_A = THREAD A runs next, STATE_B = THREAD B runs next */ 
enum { STATE_A, STATE_B } state = STATE_A; 
pthread_cond_t  condA = PTHREAD_COND_INITIALIZER; 
pthread_cond_t  condB = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; 

void *threadA() 
{ 
    int i = 0, rValue, loopNum; 

    while(i<5) 
    { 
     /* Wait for state A */ 
     pthread_mutex_lock(&mutex); 
     while (state != STATE_A) 
      pthread_cond_wait(&condA, &mutex); 
     pthread_mutex_unlock(&mutex); 

     //do stuff 
     for(loopNum = 1; loopNum <= 5; loopNum++) 
      printf("Hello %d\n", loopNum); 

     /* Set state to B and wake up thread B */ 
     pthread_mutex_lock(&mutex); 
     state = STATE_B; 
     pthread_cond_signal(&condB); 
     pthread_mutex_unlock(&mutex); 

     i++; 
    } 

    return 0; 
} 

void *threadB() 
{ 
    int n = 0, rValue; 

    while(n<5) 
    { 
     /* Wait for state B */ 
     pthread_mutex_lock(&mutex); 
     while (state != STATE_B) 
      pthread_cond_wait(&condB, &mutex); 
     pthread_mutex_unlock(&mutex); 

     //do stuff 
     printf("Goodbye\n"); 

     /* Set state to A and wake up thread A */ 
     pthread_mutex_lock(&mutex); 
     state = STATE_A; 
     pthread_cond_signal(&condA); 
     pthread_mutex_unlock(&mutex); 

     n++; 
    } 

    return 0; 
} 

pamiętać, że korzystanie z dwóch zmiennych warunkowych condA i condB jest niepotrzebny tutaj - kod będzie tak samo poprawne, jeśli tylko jedna zmienna warunek był używany zamiast.

+4

Ah, żenującego błędu z pętlą while! Dziękuję za informacje dotyczące używania stanów współdzielonych, rozumiem uzasadnienie ich użycia.Jeśli chodzi o użycie jednej zmiennej warunkowej, całkowicie zgadzam się z tym, co mówisz. Wielkie dzięki za odpowiedź, czas na zakodowanie większego projektu, jeszcze raz dziękuję za szczegółową odpowiedź! –

+1

+1 ładny opis stanu współdzielonego (nazywany często "predykatem"), że pary cvar-mtx są przeznaczone do zarządzania. – WhozCraig

0

Kod faktycznie działa prawie dobrze na moim komputerze, gdy dodasz nawiasy klamrowe do pętli while.

Dodając do tego, co powiedział caf, wprowadzisz nieskończoną pętlę, gdy wątekB zostanie uruchomiony po tym, jak wątek A już wysłał sygnał warunkowy, stąd dlaczego musisz użyć stanu współdzielonego w pętli while.

Możesz wprowadzić sztuczne opóźnienie za pomocą usleep(1) na linii 47 i przekonaj się sam.