Dlaczego funkcje zmiennych warunkowych pthreads wymagają muteksu?

182

Czytam dalej pthread.h; funkcje związane ze zmienną warunkową (jak pthread_cond_wait(3)) wymagają muteksu jako argumentu. Czemu? O ile mogę powiedzieć, że zamierzam być tworzenie mutex po prostu użyć jako tego argumentu? Co powinien zrobić ten muteks?

ELLIOTTCABLE
źródło

Odpowiedzi:

194

Jest to po prostu sposób, w jaki zmienne warunkowe są (lub były pierwotnie) implementowane.

Muteks służy do ochrony samej zmiennej warunkowej . Dlatego musisz go zablokować, zanim zaczniesz czekać.

Oczekiwanie „atomowo” odblokuje muteks, umożliwiając innym dostęp do zmiennej warunkowej (do sygnalizacji). Następnie, gdy zmienna warunku zostanie zasygnalizowana lub nadana, jeden lub więcej wątków na liście oczekujących zostanie obudzonych, a muteks zostanie ponownie magicznie zablokowany dla tego wątku.

Zazwyczaj widzisz następującą operację ze zmiennymi warunku, ilustrującymi ich działanie. Poniższy przykład to wątek roboczy, który otrzymuje pracę za pośrednictwem sygnału do zmiennej warunkowej.

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            do the work.
    unlock mutex.
    clean up.
    exit thread.

Praca jest wykonywana w ramach tej pętli, pod warunkiem, że będzie dostępna po powrocie oczekiwania. Gdy wątek zostanie oflagowany, aby przestał działać (zwykle przez inny wątek ustawiający warunek wyjścia, a następnie uruchamiający zmienną warunkową, aby obudzić ten wątek), pętla zostanie zamknięta, muteks zostanie odblokowany i ten wątek zostanie zamknięty.

Powyższy kod jest modelem dla jednego konsumenta, ponieważ muteks pozostaje zablokowany podczas pracy. W przypadku odmiany dla wielu konsumentów możesz użyć jako przykładu :

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            copy work to thread local storage.
            unlock mutex.
            do the work.
            lock mutex.
    unlock mutex.
    clean up.
    exit thread.

co pozwala innym konsumentom otrzymywać pracę, podczas gdy ten wykonuje pracę.

Zmienna warunkowa odciąża cię od odpytywania jakiegoś warunku, a zamiast tego pozwala innemu wątkowi powiadomić cię, gdy coś się wydarzy. Inny wątek może powiedzieć, że ten wątek jest dostępny, jak następuje:

lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

Ogromna większość często błędnie nazywanych fałszywymi pobudkami była generalnie zawsze dlatego, że w ich pthread_cond_waitwywołaniu (transmisji) zasygnalizowano wiele wątków , jeden wrócił z muteksem, wykonał pracę, a następnie czekał ponownie.

Wtedy drugi sygnalizowany wątek mógłby wyjść, gdy nie było żadnej pracy do wykonania. Trzeba było więc mieć dodatkową zmienną wskazującą, że należy wykonać pracę (była z natury chroniona mutex za pomocą pary condvar / mutex tutaj - inne wątki musiały zablokować muteks przed jego zmianą).

To było technicznie możliwe do nitki, aby powrócić ze stanu oczekiwania bez wyrzucony przez inny proces (jest to prawdziwe fałszywe pobudki), ale we wszystkich moich wielu lat pracuje nad pthreads, zarówno w rozwoju / usługa kodu i jako użytkownik z nich nigdy nie otrzymałem żadnego z nich. Może to tylko dlatego, że HP miał przyzwoitą implementację :-)

W każdym razie ten sam kod, który obsługiwał błędną skrzynkę, również obsługiwał autentyczne fałszywe pobudki, ponieważ flaga dostępności pracy nie byłaby dla nich ustawiona.

paxdiablo
źródło
3
„zrób coś” nie powinno znajdować się w pętli while. Chcesz, aby pętla while sprawdzała tylko warunek, w przeciwnym razie możesz również „zrobić coś”, jeśli wystąpi fałszywy pobudka.
nos
1
nie, obsługa błędów ma drugie znaczenie. Dzięki pthreads możesz się obudzić bez wyraźnego powodu (fałszywy pobudka) i bez żadnego błędu. Dlatego po przebudzeniu musisz ponownie sprawdzić „stan”.
nos
1
Nie jestem pewien, czy rozumiem. Miałem taką samą reakcję jak nos ; dlaczego jest do somethingw whilepętli?
ELLIOTTCABLE
1
Być może nie wyjaśniam tego wystarczająco jasno. Pętla nie ma czekać, aż praca będzie gotowa, więc możesz to zrobić. Pętla jest główną „nieskończoną” pętlą roboczą. Jeśli wrócisz z cond_wait i ustawisz flagę pracy, wykonasz pracę, a następnie zapętlisz się ponownie. „while some condition” będzie fałszem tylko wtedy, gdy chcesz, aby wątek przestał działać, w którym momencie zwolni muteks i najprawdopodobniej wyjdzie.
paxdiablo
7
@stefaanv „muteks nadal chroni zmienną warunkową, nie ma innego sposobu, aby ją chronić”: muteks nie chroni zmiennej warunkowej; ma to na celu ochronę danych predykatów , ale myślę, że wiesz to po przeczytaniu komentarza, który nastąpił po tym stwierdzeniu. Możesz legalnie zasygnalizować zmienną warunkową, w pełni wspieraną przez implementacje, po odblokowaniu muteksa owijającego predykat, aw rzeczywistości w niektórych przypadkach złagodzisz rywalizację.
WhozCraig
59

Zmienna warunku jest dość ograniczona, jeśli można tylko zasygnalizować warunek, zwykle trzeba obsłużyć niektóre dane związane z sygnalizowanym warunkiem. Sygnalizacja / pobudka muszą być wykonywane atomowo, aby osiągnąć to bez wprowadzania warunków wyścigu lub być zbyt skomplikowane

pthreads może również dać ci, z raczej technicznych powodów, fałszywe przebudzenie . Oznacza to, że musisz sprawdzić predykat, aby mieć pewność, że warunek został rzeczywiście zasygnalizowany - i odróżnić go od fałszywego budzenia. Sprawdzanie takiego stanu pod kątem oczekiwania na jego ochronę musi być strzeżone - więc zmienna warunku potrzebuje sposobu na atomowe oczekiwanie / budzenie podczas blokowania / odblokowywania muteksu strzegącego tego warunku.

Rozważ prosty przykład, w którym otrzymasz powiadomienie, że niektóre dane są generowane. Być może inny wątek utworzył dane, które chcesz, i ustawił wskaźnik na te dane.

Wyobraź sobie, że wątek producenta przekazuje dane do innego wątku konsumenta za pomocą wskaźnika „some_data”.

while(1) {
    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    char *data = some_data;
    some_data = NULL;
    handle(data);
}

naturalnie miałbyś dużo warunków wyścigowych, co by było, gdyby drugi wątek zrobił się some_data = new_datazaraz po przebudzeniu, ale zanim to zrobiłeśdata = some_data

Nie możesz tak naprawdę stworzyć własnego muteksu, aby strzec tej sprawy .eg

while(1) {

    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    pthread_mutex_lock(&mutex);
    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

Nie zadziała, wciąż istnieje szansa na wyścig między przebudzeniem a złapaniem muteksu. Umieszczenie muteksu przed pthread_cond_wait nie pomaga, ponieważ teraz będziesz trzymać muteks podczas oczekiwania - tj. Producent nigdy nie będzie w stanie złapać muteksu. (zwróć uwagę, że w tym przypadku możesz utworzyć drugą zmienną warunkową, aby zasygnalizować producentowi, że skończyłeś some_data- choć stanie się to skomplikowane, szczególnie jeśli chcesz wielu producentów / konsumentów).

Dlatego potrzebujesz sposobu na atomowe zwolnienie / złapanie muteksu podczas oczekiwania / budzenia się z tego stanu. To właśnie robią zmienne warunkowe pthread, a oto co byś zrobił:

while(1) {
    pthread_mutex_lock(&mutex);
    while(some_data == NULL) { // predicate to acccount for spurious wakeups,would also 
                               // make it robust if there were several consumers
       pthread_cond_wait(&cond,&mutex); //atomically lock/unlock mutex
    }

    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

(producent musiałby oczywiście podjąć takie same środki ostrożności, zawsze strzegąc „some_data” za pomocą tego samego muteksu i upewniając się, że nie nadpisze some_data, jeśli some_data jest obecnie! = NULL)

nos
źródło
Czy nie powinna while (some_data != NULL)być pętlą „do-while”, aby przynajmniej raz czekała na zmienną warunku?
Sędzia Maygarden,
3
Nie. To, na co tak naprawdę czekasz, to że „some_data” ma wartość inną niż null. Jeśli nie jest to zero za pierwszym razem, świetnie, trzymasz muteks i możesz bezpiecznie korzystać z danych. Gdybyś miał pętlę „do / while”, przegapiłbyś powiadomienie, gdyby ktoś zasygnalizował zmienną warunkową, zanim na nią poczekasz (nie przypomina to zdarzeń znalezionych w win32, które pozostają sygnalizowane, dopóki ktoś na nie nie poczeka)
nr
4
Właśnie natknąłem się na to pytanie i szczerze mówiąc, dziwne jest, że ta poprawna odpowiedź ma o wiele mniej punktów niż odpowiedź paxdiablo, która ma wyraźne wady (atomowość jest nadal potrzebna, muteks jest potrzebny tylko do spełnienia warunku, nie do obsługi lub powiadamiania). Myślę, że tak właśnie działa stackoverflow ...
stefaanv
@stefaanv, jeśli chcesz wyszczególnić wady, jako komentarze do mojej odpowiedzi, więc zobaczę je w odpowiednim czasie, a nie miesiące później :-), chętnie je naprawię. Twoje krótkie frazy tak naprawdę nie dostarczają mi wystarczająco dużo szczegółów, aby zrozumieć, co próbujesz powiedzieć.
paxdiablo
1
@nos, nie powinno while(some_data != NULL)być while(some_data == NULL)?
Eric Z
30

Zmienne warunkowe POSIX są bezstanowe. Twoim obowiązkiem jest utrzymanie stanu. Ponieważ stan będzie dostępny zarówno dla wątków oczekujących, jak i wątków, które każą innym wątkom przestać czekać, musi być chroniony przez muteks. Jeśli uważasz, że możesz używać zmiennych warunkowych bez muteksu, to nie zrozumiałeś, że zmienne warunkowe są bezstanowe.

Zmienne warunkowe są zbudowane wokół warunku. Wątki oczekujące na zmienną warunku oczekują na pewien warunek. Wątki sygnalizujące zmienne warunku zmieniają ten warunek. Na przykład wątek może oczekiwać na dostarczenie niektórych danych. Inny wątek może zauważyć, że dane dotarły. „Dane dotarły” jest warunkiem.

Oto klasyczne zastosowanie uproszczonej zmiennej warunkowej:

while(1)
{
    pthread_mutex_lock(&work_mutex);

    while (work_queue_empty())       // wait for work
       pthread_cond_wait(&work_cv, &work_mutex);

    work = get_work_from_queue();    // get work

    pthread_mutex_unlock(&work_mutex);

    do_work(work);                   // do that work
}

Zobacz, jak wątek czeka na pracę. Praca jest chroniona przez mutex. Oczekiwanie zwalnia muteks, aby inny wątek mógł dać temu wątkowi trochę pracy. Oto jak zostanie to zasygnalizowane:

void AssignWork(WorkItem work)
{
    pthread_mutex_lock(&work_mutex);

    add_work_to_queue(work);           // put work item on queue

    pthread_cond_signal(&work_cv);     // wake worker thread

    pthread_mutex_unlock(&work_mutex);
}

Zauważ, że potrzebujesz muteksu do ochrony kolejki roboczej. Zauważ, że sama zmienna warunku nie ma pojęcia, czy jest praca, czy nie. Oznacza to, że zmienna warunku musi być powiązana z warunkiem, warunek ten musi być utrzymywany przez twój kod, a ponieważ jest dzielony między wątkami, musi być chroniony przez muteks.

David Schwartz
źródło
1
Lub, mówiąc bardziej zwięźle, cały punkt zmiennych warunkowych ma zapewnić atomową operację „odblokuj i czekaj”. Bez muteksu nie byłoby nic do odblokowania.
David Schwartz,
Czy mógłbyś wyjaśnić znaczenie bezpaństwowca ?
snr
@ snr Nie mają żadnego stanu. Nie są „zablokowane”, „zasygnalizowane” ani „niepodpisane”. Twoim obowiązkiem jest śledzenie stanu powiązanego ze zmienną warunkową. Na przykład, jeśli zmienna warunkowa informuje wątek, gdy kolejka staje się pusta, musi być tak, że jeden wątek może sprawić, że kolejka będzie pusta, a inny wątek będzie musiał wiedzieć, kiedy kolejka stanie się pusta. To jest stan wspólny i musisz go chronić za pomocą muteksu. Możesz użyć zmiennej warunkowej, w powiązaniu z tym stanem wspólnym chronionym przez muteks, jako mechanizmu wznawiania.
David Schwartz
16

Nie wszystkie funkcje zmiennych warunkowych wymagają muteksu: robią to tylko operacje oczekiwania. Sygnały i operacje rozgłoszeniowe nie wymagają muteksu. Zmienna warunkowa również nie jest trwale powiązana z konkretnym muteksem; zewnętrzny muteks nie chroni zmiennej warunkowej. Jeśli zmienna warunkowa ma stan wewnętrzny, taki jak kolejka oczekujących wątków, musi być chroniona przez wewnętrzną blokadę wewnątrz zmiennej warunkowej.

Operacje oczekiwania łączą zmienną warunkową i muteks, ponieważ:

  • wątek zablokował muteks, ocenił pewne wyrażenie względem zmiennych współdzielonych i stwierdził, że jest ono fałszywe, tak że musi czekać.
  • wątek musi przejść atomowo od posiadania muteksu do oczekiwania pod warunkiem.

Z tego powodu operacja oczekiwania przyjmuje jako argumenty zarówno muteks, jak i warunek: tak, aby mógł zarządzać przeniesieniem atomowym wątku z posiadania muteksu do oczekiwania, tak aby wątek nie padł ofiarą przegranego wyścigu pobudki .

Utrata wyścigu po przebudzeniu nastąpi, jeśli wątek porzuci muteks, a następnie czeka na bezstanowy obiekt synchronizacji, ale w sposób, który nie jest atomowy: istnieje okno czasowe, w którym wątek nie ma już blokady i ma jeszcze nie zaczął czekać na obiekt. W tym oknie może wejść inny wątek, sprawić, że oczekiwany warunek będzie prawdziwy, zasygnalizować bezstanową synchronizację, a następnie zniknąć. Obiekt bezstanowy nie pamięta, że ​​został zasygnalizowany (jest bezstanowy). Tak więc oryginalny wątek zasypia na bezstanowym obiekcie synchronizacji i nie budzi się, mimo że warunek, którego potrzebuje, już się spełnił: utracone budzenie.

Funkcje czekania zmiennej warunkowej unikają utraconego budzenia przez upewnienie się, że wątek wywołujący jest zarejestrowany, aby niezawodnie wychwycić budzenie, zanim zrezygnuje z muteksu. Byłoby to niemożliwe, gdyby funkcja oczekiwania zmiennej warunkowej nie przyjęła muteksu jako argumentu.

Kaz
źródło
Czy możesz podać odniesienie, że operacje rozgłoszeniowe nie wymagają nabycia muteksu? W MSVC transmisja jest ignorowana.
xvan
@xvan POSIX pthread_cond_broadcasti pthread_cond_signaloperacje (których dotyczy to pytanie SO) nawet nie traktują muteksu jako argumentu; tylko warunek. Specyfikacja POSIX jest tutaj . Muteks jest wspomniany tylko w odniesieniu do tego, co dzieje się w oczekujących wątkach, gdy się budzą.
Kaz
Czy mógłbyś wyjaśnić znaczenie bezpaństwowca ?
snr
1
@snr Bezstanowy obiekt synchronizacji nie pamięta żadnego stanu związanego z sygnalizacją. Gdy zostanie zasygnalizowane, jeśli coś na niego teraz czeka, budzi się, w przeciwnym razie budzenie zostanie zapomniane. Zmienne warunkowe są takie bezstanowe. Stan konieczny do zapewnienia niezawodności synchronizacji jest utrzymywany przez aplikację i chroniony przez muteks, który jest używany w połączeniu ze zmiennymi warunku, zgodnie z poprawnie zapisaną logiką.
Kaz
7

Nie uważam, aby inne odpowiedzi były tak zwięzłe i czytelne jak ta strona . Normalnie kod oczekiwania wygląda mniej więcej tak:

mutex.lock()
while(!check())
    condition.wait()
mutex.unlock()

Istnieją trzy powody, aby zawrzeć wait()mutex:

  1. bez muteksu inny wątek mógłby signal()przedtem wait()i tęsknilibyśmy za tym przebudzeniem.
  2. zwykle check()zależy od modyfikacji z innego wątku, więc i tak potrzebujesz wzajemnego wykluczenia.
  3. aby upewnić się, że wątek o najwyższym priorytecie działa jako pierwszy (kolejka dla muteksu pozwala harmonogramowi zdecydować, kto będzie następny).

Trzeci punkt nie zawsze jest problemem - kontekst historyczny jest powiązany z artykułem z tą rozmową .

O tym mechanizmie często wspominane są fałszywe pobudki (tzn. Oczekujący wątek jest budzony bez signal()wywoływania). Jednak takie zdarzenia są obsługiwane przez zapętlone check().

Sam Brightman
źródło
4

Zmienne warunkowe są powiązane z muteksem, ponieważ jest to jedyny sposób, w jaki można uniknąć rasy, której ma unikać.

// incorrect usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    pthread_mutex_unlock(&mutex);
    if (ready) {
        doWork();
    } else {
        pthread_cond_wait(&cond1); // invalid syntax: this SHOULD have a mutex
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond1);

Now, lets look at a particularly nasty interleaving of these operations

pthread_mutex_lock(&mutex);
bool ready = protectedReadyToRunVariable;
pthread_mutex_unlock(&mutex);
                                 pthread_mutex_lock(&mutex);
                                 protectedReadyToRuNVariable = true;
                                 pthread_mutex_unlock(&mutex);
                                 pthread_cond_signal(&cond1);
if (ready) {
pthread_cond_wait(&cond1); // uh o!

W tym momencie nie ma wątku, który zasygnalizuje zmienną warunkową, więc wątek1 będzie czekać wiecznie, mimo że chronionaReadyToRunVariable mówi, że jest gotowy do pracy!

Jedynym sposobem na obejście tego jest to, że zmienne warunkowe atomowo zwalniają muteks, jednocześnie zaczynając czekać na zmienną warunkową. Dlatego funkcja cond_wait wymaga muteksu

// correct usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    if (ready) {
        pthread_mutex_unlock(&mutex);
        doWork();
    } else {
        pthread_cond_wait(&mutex, &cond1);
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
   pthread_cond_signal(&mutex, &cond1);
pthread_mutex_unlock(&mutex);
Cort Ammon
źródło
3

Muteks powinien być zablokowany podczas połączenia pthread_cond_wait; kiedy go nazwiesz, atomowo odblokowuje muteks, a następnie blokuje warunek. Po zasygnalizowaniu warunku atomowo blokuje go ponownie i wraca.

Pozwala to na implementację przewidywalnego szeregowania, jeśli jest to pożądane, w tym sensie, że wątek, który wykonuje sygnalizację, może poczekać, aż muteks zostanie zwolniony, aby wykonać przetwarzanie, a następnie zasygnalizować warunek.

Bursztyn
źródło
Więc… czy jest jakiś powód, dla którego mam nie tylko pozostawić muteks zawsze odblokowany, a następnie zablokować go tuż przed czekaniem, a następnie odblokować zaraz po zakończeniu oczekiwania?
ELLIOTTCABLE
Muteks rozwiązuje również niektóre potencjalne rasy między wątkami oczekującymi i sygnalizującymi. tak długo, jak muteks jest zawsze zablokowany podczas zmiany stanu i sygnalizacji, nigdy nie zauważysz braku sygnału i snu na zawsze
Hasturkun
Więc… powinienem najpierw poczekać na muteks na muteksie conditionvar, zanim zacznę czekać na conditionvar? Nie jestem pewien, czy rozumiem w ogóle.
ELLIOTTCABLE
2
@elliottcable: Bez trzymania muteksu, skąd możesz wiedzieć, czy powinieneś, czy nie powinieneś czekać? Co się stanie, jeśli to, na co czekasz, właśnie się wydarzyło?
David Schwartz,
1

Zrobiłem ćwiczenie w klasie, jeśli chcesz prawdziwy przykład zmiennej warunkowej:

#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "unistd.h"

int compteur = 0;
pthread_cond_t varCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_compteur;

void attenteSeuil(arg)
{
    pthread_mutex_lock(&mutex_compteur);
        while(compteur < 10)
        {
            printf("Compteur : %d<10 so i am waiting...\n", compteur);
            pthread_cond_wait(&varCond, &mutex_compteur);
        }
        printf("I waited nicely and now the compteur = %d\n", compteur);
    pthread_mutex_unlock(&mutex_compteur);
    pthread_exit(NULL);
}

void incrementCompteur(arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_compteur);

            if(compteur == 10)
            {
                printf("Compteur = 10\n");
                pthread_cond_signal(&varCond);
                pthread_mutex_unlock(&mutex_compteur);
                pthread_exit(NULL);
            }
            else
            {
                printf("Compteur ++\n");
                compteur++;
            }

        pthread_mutex_unlock(&mutex_compteur);
    }
}

int main(int argc, char const *argv[])
{
    int i;
    pthread_t threads[2];

    pthread_mutex_init(&mutex_compteur, NULL);

    pthread_create(&threads[0], NULL, incrementCompteur, NULL);
    pthread_create(&threads[1], NULL, attenteSeuil, NULL);

    pthread_exit(NULL);
}
Central Thinking Unit
źródło
1

Wydaje się, że jest to konkretna decyzja projektowa, a nie potrzeba koncepcyjna.

Według dokumentów pthreads powodem, dla którego muteks nie został rozdzielony, jest znaczna poprawa wydajności, łącząc je i oczekują, że z powodu typowych warunków rasowych, jeśli nie użyjesz muteksu, prawie zawsze tak będzie.

https://linux.die.net/man/3/pthread_cond_wait

Cechy muteksów i zmiennych warunkowych

Zasugerowano, aby pozyskiwanie i uwalnianie mutexów było oddzielone od warunku oczekiwania. Zostało to odrzucone, ponieważ w rzeczywistości połączony charakter operacji ułatwia wdrażanie w czasie rzeczywistym. Implementacje te mogą atomowo przenosić wątek o wysokim priorytecie między zmienną warunkową a muteksem w sposób przezroczysty dla osoby wywołującej. Może to zapobiec dodatkowym przełączeniom kontekstu i zapewnić bardziej deterministyczne pozyskiwanie muteksu, gdy sygnalizowany jest wątek oczekujący. Tak więc kwestie związane z uczciwością i priorytetem mogą być rozwiązywane bezpośrednio przez dyscyplinę planowania. Ponadto bieżąca operacja oczekiwania warunku odpowiada istniejącej praktyce.

Catskul
źródło
0

Istnieje mnóstwo egzegezy na ten temat, ale chcę to uosabiać za pomocą następującego przykładu.

1 void thr_child() {
2    done = 1;
3    pthread_cond_signal(&c);
4 }

5 void thr_parent() {
6    if (done == 0)
7        pthread_cond_wait(&c);
8 }

Co jest nie tak z fragmentem kodu? Zastanów się trochę, zanim przejdziesz dalej.


Sprawa jest naprawdę subtelna. Jeśli rodzic przywoła, thr_parent()a następnie zweryfikuje wartość done, zobaczy, że tak, 0i spróbuje iść spać. Ale tuż przed wezwaniem do przejścia w tryb uśpienia rodzic zostaje przerwany między wierszami 6-7, a dziecko biegnie. Dziecko zmienia zmienną stanu donena 1i sygnalizuje, ale żaden wątek nie czeka i dlatego wątek nie jest budzony. Kiedy rodzic biegnie ponownie, śpi wiecznie, co jest naprawdę rażące.

Co się stanie, jeśli zostaną przeprowadzone indywidualnie, gdy nabyte zamki?

snr
źródło