Szukaj…


Wprowadzenie

Zmienne warunkowe są przydatne w przypadkach, gdy wątek ma czekać na coś, co dzieje się w innym wątku. Na przykład, w scenariuszu producenta / konsumenta z jednym lub większą liczbą wątków produkujących i jednym wątkiem konsumpcyjnym, zmienne warunkowe mogą być używane do sygnalizowania konsumentowi wątku, że nowe dane są dostępne.

Uwagi

Ogólny proces

Oczekiwanie na zmienną warunkową (queueCond w przykładzie producent / konsument) jest zawsze sprzężone z mutexem (queueMutex w przykładzie producent / konsument) i zawsze powinno być również połączone ze „normalną” zmienną stanu (queue.empty ( ) w przykładzie producent / konsument). Przy prawidłowym użyciu zapewnia to, że konsument nie przegapi żadnych nowych danych.

Ogólnie rzecz biorąc, proces powinien być:

  • W przypadku wątku sygnalizacyjnego:
    1. Zablokuj muteks
    2. Zaktualizuj dowolne zmienne danych i stanów
    3. Sygnalizuj zmienną warunkową
    4. Odblokuj muteks
  • Dla oczekującego wątku:
    1. Zablokuj muteks
    2. Zrobić while pętli na zmiennej stanu, pętli tak długo jak dane nie jest gotowy
    3. W while pętli, czy czekać na zmiennej warunkowej z pthread_cond_wait()
    4. Gdy while wyjścia pętli, jesteśmy pewni, że nowe dane jest gotowy i że mutex jest zablokowany
    5. Zrób coś z danymi
    6. Odblokuj muteks i powtórz

W tym schemacie, bez względu na to, kiedy zaplanowane są wątki sygnalizacyjne i oczekujące, wątek oczekujący nigdy nie przegapi danych (jak w tym przypadku, nigdy nie utknie czekając na zawsze z gotowymi ważnymi danymi). Można to zrealizować, ręcznie próbując wykonać kroki dla wątku sygnalizacyjnego, rejestrując stany muteksu, warunku i zmiennych stanu dla każdego z kroków w oczekującym wątku.

pthread_cond_wait i mutex

Aby ułatwić powyższy proces, konieczne jest wywołanie pthread_cond_wait() przy zablokowanym muteksie. Po wywołaniu pthread_cond_wait() odblokuje muteks przed uśpieniem wątku, a tuż przed powrotem z jakiegokolwiek powodu muteks zostanie ponownie zablokowany. Oznacza to również, że jeśli jakiś inny wątek ma aktualnie zablokowany muteks, pthread_cond_wait() będzie czekał na odblokowanie muteksu i dopóki oczekujący wątek nie będzie mógł faktycznie uzyskać muteksu - będzie walczył z nim wraz z innymi wątkami próbującymi się zablokować muteks w tym samym czasie.

Sztuczne budzenie

Ponadto, może się wydawać, jakby while pętli czeka na zmiennej stanu może być zastąpiony przez prosty if stwierdzenia. Jednakże, while pętla jest potrzebna, jako standard POSIX pozwala pthread_cond_wait() , aby zrobić tak zwanych „fałszywych” wybudzeń podczas czekania, bez faktycznie sygnalizowane. Dlatego kod musi ponownie sprawdzić zmienną stanu, aby sprawdzić, czy pthread_cond_wait() zwróciło z powodu faktycznej sygnalizacji lub z powodu jednego z tych fałszywych wybudzeń.

Przykład producenta / konsumenta

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);
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow