Zmienna warunkowa a semafor

Odpowiedzi:

207

Zamki służą do wzajemnego wykluczania. Jeśli chcesz mieć pewność, że fragment kodu jest niepodzielny, umieść wokół niego blokadę. Teoretycznie można by to zrobić za pomocą semafora binarnego, ale to jest szczególny przypadek.

Semafory i zmienne warunkowe opierają się na wzajemnym wykluczaniu zapewnianym przez blokady i służą do zapewniania zsynchronizowanego dostępu do współdzielonych zasobów. Mogą być używane do podobnych celów.

Zmienna warunkowa jest zwykle używana, aby uniknąć zajętego oczekiwania (wielokrotne zapętlanie podczas sprawdzania warunku) podczas oczekiwania na udostępnienie zasobu. Na przykład, jeśli masz wątek (lub wiele wątków), który nie może kontynuować, dopóki kolejka nie będzie pusta, podejście do zajętego oczekiwania polegałoby na zrobieniu czegoś takiego:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

Problem polega na tym, że marnujesz czas procesora, kilkakrotnie sprawdzając stan tego wątku. Dlaczego zamiast tego nie mieć zmiennej synchronizacyjnej, która może być sygnalizowana, aby poinformować wątek, że zasób jest dostępny?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Prawdopodobnie będziesz mieć wątek gdzieś indziej, który wyciąga rzeczy z kolejki. Kiedy kolejka jest pusta, może zadzwonić, syncVar.signal()aby obudzić losowy wątek, który śpi syncVar.wait()(lub zwykle jest również signalAll()lubbroadcast() metoda , aby obudzić wszystkie oczekujące wątki).

Generalnie używam takich zmiennych synchronizacyjnych, gdy jeden lub więcej wątków oczekuje na jeden określony warunek (np. Na pustą kolejkę).

Semafory mogą być używane w podobny sposób, ale myślę, że są lepiej używane, gdy masz współdzielony zasób, który może być dostępny i niedostępny na podstawie pewnej liczby całkowitej dostępnych rzeczy. Semafory są dobre w sytuacjach producenta / konsumenta, w których producenci alokują zasoby, a konsumenci je konsumują.

Pomyśl, czy masz automat sprzedający napoje gazowane. Jest tylko jedna maszyna do napojów i jest to wspólny zasób. Masz jeden wątek, który jest sprzedawcą (producentem), który jest odpowiedzialny za przechowywanie maszyny na stanie, i N nici, którzy są kupującymi (konsumentami), którzy chcą wydobyć napoje gazowane z maszyny. Liczba napojów gazowanych w maszynie to liczba całkowita, która będzie napędzać nasz semafor.

Każdy wątek kupującego (konsumenta), który przychodzi do automatu z napojami, wywołuje down()metodę semafora , aby wziąć napój gazowany. Spowoduje to pobranie napoju gazowanego z maszyny i zmniejszenie liczby dostępnych napojów o 1. Jeśli są dostępne napoje gazowane, kod będzie po prostu działał pozadown() instrukcją bez problemu. Jeśli napoje gazowane nie są dostępne, wątek będzie spał w tym miejscu, czekając na powiadomienie o ponownym udostępnieniu napoju gazowanego (gdy w urządzeniu będzie więcej napojów gazowanych).

Dostawca (producent) nici zasadniczo czekałby na opróżnienie automatu do napojów. Sprzedawca jest powiadamiany, kiedy ostatni napój gazowany jest pobierany z automatu (a jeden lub więcej konsumentów potencjalnie czeka na ich wyjęcie). Sprzedawca uzupełniłby semafor w automacie do napojówup() metodą , dostępna liczba napojów gazowanych byłaby zwiększana za każdym razem, a tym samym oczekujące wątki konsumentów byłyby powiadamiane o dostępności większej ilości napojów gazowanych.

Te wait()i signal()metody zmiennej synchronizacji wydają się być ukryte wewnątrz down()i up()operacji semafora.

Z pewnością te dwie opcje pokrywają się. Istnieje wiele scenariuszy, w których semafor lub zmienna warunku (lub zestaw zmiennych warunkowych) mogą służyć Twoim celom. Zarówno semafory, jak i zmienne warunkowe są powiązane z obiektem blokady, którego używają do utrzymywania wzajemnego wykluczania, ale następnie zapewniają dodatkową funkcjonalność oprócz blokady do synchronizacji wykonywania wątków. To głównie do Ciebie należy ustalenie, który z nich jest najbardziej odpowiedni w Twojej sytuacji.

To niekoniecznie najbardziej techniczny opis, ale to ma sens w mojej głowie.

Brent pisze kod
źródło
9
Świetna odpowiedź, chciałbym dodać z innych tak odpowiedzi: Semafor służy do kontrolowania ilości wykonywanych wątków. Będzie ustalony zestaw zasobów. Liczba zasobów zostanie zmniejszona za każdym razem, gdy wątek jest właścicielem tego samego. Gdy liczba semaforów osiągnie 0, żadne inne wątki nie mogą uzyskać zasobu. Wątki są blokowane do czasu, gdy inne wątki są właścicielami wydań zasobów. Krótko mówiąc, główna różnica polega na tym, ile wątków może jednocześnie uzyskać zasób? Muteks - to JEDEN. Semaphore - its DEFINED_COUNT, (as many as semaphore count)
berkay
10
Aby wyjaśnić, dlaczego istnieje pętla while zamiast prostego if: coś, co nazywa się budzeniem spurios . Cytując ten artykuł z Wikipedii : „Jednym z powodów tego jest fałszywe wybudzenie; to znaczy wątek może zostać wybudzony ze stanu oczekiwania, mimo że żaden wątek nie zasygnalizował zmiennej warunku”
Vladislavs Burakovs
3
@VladislavsBurakovs Słuszna uwaga! Myślę, że jest to również pomocne w przypadku, gdy transmisja budzi więcej wątków niż jest dostępnych zasobów (np. Transmisja budzi 3 wątki, ale w kolejce są tylko 2 elementy).
Brent pisze kod
Chciałbym zagłosować za twoją odpowiedzią, dopóki kolejka nie będzie pełna;) Idealna odpowiedź. Ten kod może pomóc w ustaleniu semaforów csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME
3
@VladislavsBurakovs Aby trochę wyjaśnić, powodem, dla którego stan może nadal być fałszywy dla wątku, który właśnie się obudził (powodując fałszywe wybudzenie), jest to, że mogło nastąpić przełączenie kontekstu, zanim wątek miał szansę sprawdzić stan ponownie, gdzie inny zaplanowany wątek sprawił, że warunek był fałszywy. To jeden z powodów, dla których znam fałszywe przebudzenie, nie wiem, czy jest ich więcej.
maks.
52

Pokażmy, co kryje się pod maską.

Zmienna warunkowa jest zasadniczo kolejką oczekiwania , która obsługuje operacje blokowania-oczekiwania i wznowienia, tj. Można umieścić wątek w kolejce oczekiwania i ustawić jego stan na BLOK, a następnie pobrać z niej wątek i ustawić jego stan na GOTOWY.

Zwróć uwagę, że aby użyć zmiennej warunkowej, potrzebne są dwa inne elementy:

  • warunek (zwykle realizowany przez sprawdzenie flagi lub licznika)
  • muteks, który chroni stan

Protokół staje się wtedy:

  1. nabyć mutex
  2. sprawdzić stan
  3. zablokuj i zwolnij muteks, jeśli warunek jest prawdziwy, w przeciwnym razie zwolnij muteks

Semafor jest w zasadzie licznikiem + muteksem + kolejką oczekiwania. I może być używany bez zewnętrznych zależności. Możesz go używać jako muteksu lub jako zmiennej warunkowej.

Dlatego semafor można traktować jako bardziej wyrafinowaną strukturę niż zmienna warunkowa, podczas gdy ta ostatnia jest lżejsza i bardziej elastyczna.

cucufrog
źródło
mutex może być postrzegany jako zmienna warunkowa, jej warunkiem jest to, czy ma być wstrzymany, czy nie.
宏杰 李
18

Semafory mogą służyć do implementacji wyłącznego dostępu do zmiennych, jednak mają służyć do synchronizacji. Z drugiej strony muteksy mają semantykę ściśle związaną z wzajemnym wykluczaniem: tylko proces, który zablokował zasób, może go odblokować.

Niestety nie możesz zaimplementować synchronizacji z muteksami, dlatego mamy zmienne warunkowe. Zauważ również, że zmiennymi warunkowymi możesz odblokować wszystkie oczekujące wątki w tej samej chwili, używając odblokowania transmisji. Nie można tego zrobić za pomocą semaforów.

Dacav
źródło
9

zmienne semafora i warunku są bardzo podobne i są używane głównie do tych samych celów. Istnieją jednak drobne różnice, które mogą sprawić, że jeden będzie lepszy. Na przykład, aby zaimplementować synchronizację barier, nie można byłoby użyć semafora, ale zmienna warunkowa jest idealna.

Synchronizacja z barierami polega na tym, że chcesz, aby wszystkie twoje wątki czekały, aż wszyscy dotrą do określonej części funkcji wątku. można to zrealizować poprzez posiadanie zmiennej statycznej, która jest początkowo wartością wszystkich wątków zmniejszanych przez każdy wątek, gdy osiągnie tę barierę. oznaczałoby to, że chcemy, aby każdy wątek spał, dopóki nie nadejdzie ostatni. semafor zrobiłby dokładnie odwrotnie! z semaforem, każdy wątek działałby dalej, a ostatni wątek (który ustawi wartość semafora na 0) przejdzie w stan uśpienia.

z drugiej strony zmienna warunkowa jest idealna. kiedy każdy wątek dociera do bariery, sprawdzamy, czy nasz licznik statyczny wynosi zero. jeśli nie, ustawiamy wątek w stan uśpienia za pomocą funkcji wait zmiennej warunku. kiedy ostatni wątek dotrze do bariery, wartość licznika zostanie zmniejszona do zera, a ten ostatni wątek wywoła funkcję sygnału zmiennej warunku, która obudzi wszystkie inne wątki!

Danielle
źródło
1

I zapisuję zmienne warunków w ramach synchronizacji monitora. Generalnie postrzegałem semafory i monitory jako dwa różne style synchronizacji. Istnieją różnice między nimi pod względem tego, ile danych stanu jest z natury przechowywanych i jak chcesz modelować kod - ale tak naprawdę nie ma żadnego problemu, który można rozwiązać jednym, a nie drugim.

Mam tendencję do kodowania w kierunku formy monitora; w większości języków, w których pracuję, sprowadza się to do muteksów, zmiennych warunkowych i niektórych pomocniczych zmiennych stanu. Ale semafory też by to zrobiły.

Justin R.
źródło
2
To byłaby lepsza odpowiedź, gdybyś wyjaśnił, czym jest „formularz monitorowania”.
Steven Lu
0

Za wykorzystanie mutexi conditional variablessą dziedziczone semaphore.

  • Dla mutextego, semaphorekorzysta z dwóch stanów: 0, 1
  • Na condition variablestej semaphore kasie zastosowań.

Są jak cukier syntaktyczny

wodny
źródło
W bibliotece std C ++ wszystkie są obiektami dzielnicowymi, wszystkie zaimplementowane przy użyciu interfejsów API specyficznych dla platformy. Z pewnością semafor odblokuje liczbę sygnalizowanych sygnałów, zmienna warunkowa może być sygnalizowana wiele razy, ale odblokowana tylko raz. Dlatego wair przyjmuje muteks jako parametr.
doron