Cytując stronę podręcznika:
Podczas korzystania ze zmiennych warunkowych zawsze istnieje predykat boolowski obejmujący zmienne współdzielone skojarzone z każdym warunkiem oczekiwania, który jest prawdą, jeśli wątek powinien kontynuować. Mogą wystąpić fałszywe wybudzenia z funkcji pthread_cond_timedwait () lub pthread_cond_wait (). Ponieważ powrót z pthread_cond_timedwait () lub pthread_cond_wait () nie implikuje nic o wartości tego predykatu, predykat powinien zostać ponownie oceniony po takim powrocie.
Więc pthread_cond_wait
może wrócić, nawet jeśli tego nie zasygnalizowałeś. Przynajmniej na pierwszy rzut oka wydaje się to dość okropne. To byłoby jak funkcja, która losowo zwraca niewłaściwą wartość lub zwraca losowo, zanim faktycznie osiągnie właściwą instrukcję powrotu. Wygląda na poważny błąd. Ale fakt, że zdecydowali się udokumentować to na stronie podręcznika zamiast naprawiać, wydaje się wskazywać, że istnieje uzasadniony powód, dla którego pthread_cond_wait
budzą się fałszywie. Przypuszczalnie jest coś nieodłącznego w tym, jak to działa, co sprawia, że nie można temu zaradzić. Pytanie brzmi: co.
Dlaczego nie pthread_cond_wait
wrócić fałszywie? Dlaczego nie może zagwarantować, że obudzi się tylko wtedy, gdy zostanie odpowiednio zasygnalizowana? Czy ktoś może wyjaśnić powód jego fałszywego zachowania?
pthread_cond_(timed)wait
: „Jeśli sygnał jest dostarczany ... wątek wznawia oczekiwanie na zmienną warunku, tak jakby była nie zostanie przerwany lub zwróci zero z powodu fałszywego wybudzenia ”. Inne funkcje blokujące wskazują,EINTR
kiedy są przerywane przez sygnał (np.read
) Lub są wymagane do wznowienia (nppthread_mutex_lock
.). Więc gdyby nie było innych powodów fałszywego wybudzenia,pthread_cond_wait
można by je zdefiniować w ten sposób.Odpowiedzi:
Następujące wyjaśnienie podaje David R. Butenhof w „Programming with POSIX Threads” (s. 80):
W poniższej dyskusji comp.programming.threads rozwija myślenie stojące za projektem:
źródło
Istnieją co najmniej dwie rzeczy, które może oznaczać `` fałszywe wybudzenie '':
pthread_cond_wait
może powrócić z wywołania, nawet jeśli nie wystąpiło żadne wywołaniepthread_call_signal
lubpthread_cond_broadcast
warunek.pthread_cond_wait
powrocie z powodu wywołaniapthread_cond_signal
lubpthread_cond_broadcast
, jednak po ponownym uzyskaniu muteksu okazuje się, że bazowy predykat nie jest już prawdziwy.Ale ten drugi przypadek może wystąpić, nawet jeśli implementacja zmiennej warunkowej nie zezwala na pierwszy przypadek. Rozważmy kolejkę konsumenta producenta i trzy wątki.
pthread_cond_wait
i blokami w wywołaniu oczekującym na sygnał / emisję.Ponieważ więc już zawsze musisz sprawdzić predykat w pętli, nie ma znaczenia, czy podstawowe zmienne warunku mogą mieć inne rodzaje fałszywych wybudzeń.
źródło
pthread_cond_signal/broadcast
i nie będziesz w stanie tego zrobić, dopóki mutex nie zostanie odblokowany przez wywołaniepthread_cond_wait
.Sekcja „Wielokrotne przebudzenia według sygnału stanu” w pthread_cond_signal zawiera przykładową implementację pthread_cond_wait i pthread_cond_signal, które obejmują fałszywe wybudzania.
źródło
Chociaż nie sądzę, aby było to brane pod uwagę w czasie projektowania, oto rzeczywisty powód techniczny: w połączeniu z anulowaniem nici istnieją warunki, w których wybranie opcji budzenia się „niejawnie” może być absolutnie konieczne, przynajmniej jeśli nie są gotowi nałożyć bardzo duże ograniczenia na to, jakie rodzaje strategii wdrożeniowych są możliwe.
Kluczowy problem polega na tym, że jeśli wątek działa na anulowanie, gdy jest zablokowany
pthread_cond_wait
, efekty uboczne muszą wyglądać tak, jakby nie zużywał żadnego sygnału ze zmiennej warunku. Jednak trudno jest (i bardzo ogranicza) upewnić się, że sygnał nie został już zużyty, gdy zaczynasz działać w trybie anulowania, a na tym etapie może być niemożliwe „ponowne przesłanie” sygnału do zmiennej warunku, ponieważ możesz znajdować się w sytuacji, w której osoba dzwoniącapthread_cond_signal
jest już usprawiedliwiona, że zniszczyła condvar i uwolniła pamięć, w której się znajdował.Dodatek na fałszywe przebudzenie zapewnia łatwe wyjście. Zamiast kontynuować działanie na anulowanie, gdy nadejdzie, gdy jest zablokowane na zmiennej warunkowej, jeśli być może już zużyłeś sygnał (lub jeśli chcesz być leniwy, bez względu na wszystko), możesz zamiast tego zadeklarować fałszywe przebudzenie, i wróć z sukcesem. Nie koliduje to w ogóle z operacją anulowania, ponieważ poprawny dzwoniący po prostu zareaguje na oczekujące anulowanie przy następnym zapętleniu i
pthread_cond_wait
ponownym wywołaniu .źródło