Istnieje szkoła myśli, która stwierdza, że powinieneś use locks as frugally as you can. To znaczy. nigdy nie używaj zamka, jeśli możesz go ominąć, a jeśli musisz go użyć, zablokuj go na minimalny czas. Rozumowanie tego wynika z niekiedy znacznych kosztów zabrania blokady w pierwszej kolejności wraz z kosztem jednego wątku oczekującego, podczas gdy inny blokuje zasób, którego potrzebuje.
Nastąpił dostępne dla very long time, instrukcje CPU o nazwie Porównaj i ustawiona (lub CAS w skrócie) zaprojektowany, aby pomóc z tym, że w zasadzie zrobić:
if (value == providedValue) {
value = newValue;
return true;
} else {
return false;
}
instrukcje te można wykonać na maszynie -kod poziomu i są znacznie szybsze niż tworzenie blokady.
Wyobraź sobie, że chcesz dodać 1
do numeru za pomocą jednej z tych instrukcji w sposób, który będzie konsekwentnie działał poprawnie pod dużym obciążeniem równoległym.Wyraźnie można zakodować go jako:
int old = value;
if (compareAndSet(old, old+1)) {
// It worked!
} else {
// Some other thread incremented it before I got there.
}
Ale co możemy zrobić, jeśli CAS
powiodło? Zgadłeś - spróbuj jeszcze raz!
boolean succeeded = false;
do {
int old = value;
if (compareAndSet(old, old+1)) {
// It worked!
succeeded = true;
} else {
// Some other thread incremented it before I got there. Just try again.
}
} while (!succeeded);
I tam widzisz wzór, który obserwujesz.
Posługując się tym i podobnymi idiomami można zaimplementować wiele funkcji, a nawet niektóre dość skomplikowane struktury danych bez użycia blokad (powszechnie zwane Zablokuj bezpłatny). Na przykład: here to implementacja bez blokady bez zabezpieczenia Ring Buffer.
Czy compareAndSet zapewnia jakiekolwiek gwarancje przeciwko głodowi? – Random832