W artykule o tym samym tytule co tytuł tego pytania, autorzy opisują, jak zbudować nieblokowalną, liniową, wielowątkową operację CAS , używając tylko jednego słowa CAS. Najpierw wprowadzają operację podwójnego porównania-pojedynczej wymiany - RDCSS, jak następuje:
word_t RDCSS(RDCSSDescriptor_t *d) {
do {
r = CAS1(d->a2, d->o2, d);
if (IsDescriptor(r)) Complete(r);
} while (IsDescriptor(r));
if (r == d->o2) Complete(d); // !!
return r;
}
void Complete(RDCSSDescriptor_t *d) {
v = *(d->a1);
if (v == d->o1) CAS1(d->a2, d, d->n2);
else CAS1(d->a2, d, d->o2);
}
gdzie RDCSSDescriptor_t
jest strukturą zawierającą następujące pola:
a1
- adres pierwszego warunkuo1
- wartość oczekiwana pod pierwszym adresema2
- adres drugiego warunkuo2
- wartość oczekiwana pod drugim adresemn2
- nowa wartość do zapisania pod drugim adresem
Ten deskryptor jest tworzony i inicjowany jeden raz w wątku, który inicjuje operację RDCSS - żaden inny wątek nie ma do niego odwołania, dopóki pierwszy CAS1 w funkcji nie RDCSS
powiedzie się, co czyni deskryptor osiągalnym (lub aktywnym w terminologii artykułu).
Algorytm jest następujący: zastąp drugą lokalizację pamięci deskryptorem informującym o tym, co chcesz zrobić. Następnie, biorąc pod uwagę, że deskryptor jest obecny, sprawdź pierwszą lokalizację pamięci, aby zobaczyć, czy jej wartość się zmieniła. Jeśli nie, zastąp deskryptor w drugiej lokalizacji pamięci nową wartością. W przeciwnym razie ustaw drugą lokalizację pamięci z powrotem na starą wartość.
Autorzy nie wyjaśniają, dlaczego linia z !!
komentarzem jest niezbędna w pracy. Wydaje mi się, że po tej kontroli CAS1
instrukcje w Complete
funkcji zawsze się nie udają, pod warunkiem, że nie ma równoczesnej modyfikacji. A jeśli była jednoczesna modyfikacja między czekiem a CAS w Complete
, to wątek wykonujący sprawdzenie powinien nadal zawieść ze swoim CAS w Complete
, ponieważ jednoczesna modyfikacja nie powinna używać tego samego deskryptora d
.
Moje pytanie brzmi: czy można pominąć sprawdzanie funkcji RDCSSS
, if (r == d->o2)...
gdy RDCSS nadal zachowuje semantykę instrukcji podwójnego porównania, pojedynczej zamiany, która jest linearyzowalna i bez blokady ? (linia z !!
komentarzem)
Jeśli nie, czy możesz opisać scenariusz, w którym ta linia jest rzeczywiście konieczna do zapewnienia poprawności?
Dziękuję Ci.
źródło
Odpowiedzi:
W równoczesnym środowisku uruchomieniowym proste rzeczy mogą wydawać się dziwne ... mam nadzieję, że to może pomóc.
Mamy wbudowany ATOMIC CAS1 o tej semantycznej:
Musimy zdefiniować funkcję ATOMIC RDCSS za pomocą CAS1 i mieć następujący semantyczny:
Intuicyjnie: musimy KONKURENCYJNIE zmieniać wartość w addr2 tylko, jeśli * addr1 == oldval1 ... jeśli inny wątek go zmienia, możemy pomóc drugiemu wątkowi w zakończeniu operacji, a następnie możemy spróbować ponownie.
Do zdefiniowania CASN zostanie użyta funkcja RDCSS (patrz artykuł). Teraz definiujemy deskryptor RDCSS w następujący sposób:
Następnie wdrażamy RDCSS w następujący sposób:
ODPOWIEDŹ NA PYTANIE
Jeśli pominiemy STEP4, to część if (... && * addr1 == oldval1) * addr2 = newval2 semantycznej części RDCSS nigdy nie zostanie wykonana (... lub lepiej: można ją wykonać w sposób przewidywalny przez inne wątki pomagające obecny).
Jak zauważyłeś w swoim komentarzu, warunek, jeśli (res == d1-> oldval2) w STEP4 jest niepotrzebny: nawet jeśli go pominiemy, oba CAS1 w Complete () zawiodą, ponieważ * (d-> addr2)! = D . Jedynym celem jest uniknięcie wywołania funkcji.
Przykład T1 = wątek 1, T2 = wątek 2:
źródło
addr2
zawierad2->newval2
. Ale wydaje mi się, że CAS1 poComplete
prostu zawiedzie, ponieważ oczekuje, że stara wartość będzie deskryptoremd1
- T1 nic nie napisze . Dobrze?