# PRW 4 ## Z1 ![](https://i.imgur.com/1iBtlBR.png) ### IMPLEMENTACJA ZAMKA DLA $N$ WĄTKÓW ```java= class Filter implements Lock { //informacje o poziomie każdego wątku int[] level; //informacje o wątku który jako ostatni zgłosił chęć zdobycia poziomu int[] victim; public Filter(int n) { level = new int[n]; victim = new int[n]; // use 1..n-1 for (int i = 0; i < n; i++) { level[i] = 0; } } public void lock() { int me = ThreadID.get(); // returns 0..n-1 for (int i = 1; i < n; i++) { // attempt to enter level i level[me] = i; victim[i] = me; // spin while conflicts exist // inny wątek jest na wyższym lub równym poziomie // i nasz wątek jako ostatni zgłosił chęć zdobycia poziomu while (( ∃ k != me) (level[k] >= i && victim[i] == me)) {}; } } public void unlock() { int me = ThreadID.get(); level[me] = 0; //zerujemy poziom wątku } } ``` ### FAKT Każdy wątek wykonujący metodę **lock()** znajduje się na jednym z $n-1$ poziomów, z których jako ostatni oznacza zajęcie zamka **Na poziomie $n-i$ znajduje się jednocześnie co najwyżej $i$ wątków.** ![](https://i.imgur.com/QoQT6BU.png) ### DOWÓD SPEŁNIENIA WARUNKU WZAJEMNEGO WYKLUCZANIA **Własności poziomów** * Przynajmniej jeden wątek próbujący wejść na poziom $l$ odnosi sukces * Przynajmniej jeden z wątków próbujących wejść w tym samym czasie na poziom $l$ zostaje zablokowany * Każdy wątek musi przejść przez $n-1$ poziomów by się dostać do sekcji krytycznej #### Dowód przez indukcję po liczbie poziomów $j$. Niech $n$ oznacza liczbę wątków. **Baza** Wszystkie $n$ wątków znajduje się na poziomie $0$ od samego początku **Krok** Załóżmy nie wprost, że nie jest spełniona własność **przynajmniej 1 wątek nie może przejść na poziom $j$**, tj. że na poziomie $j-1$ zamiast $n-(j-1)$ wątków jest ich $n-j$ **Z kodu widzimy własności:** - Niech $A$ będzie ostatnim wątkiem na poziomie $j$, który wykonał zapis w polu $victim[j]$. Wtedy dla każdego innego wątku $B$ na poziomie $j$ mamy write$_B$(victim[j]) -> write$_A$(victim[j]) - Wątek $B$ zapisuje $level[B]$ przed zapisaniem $victim[j]$ write$_B$(level[B] = j) -> write$_B$(victim[j]) -> write$_A$(victim[j]) - Wątek $A$ odczytuje $level[B]$ po zapisaniu $victim[j]$ write$_B$(level[B] = j) -> write$_B$(victim[j]) -> write$_A$(victim[j]) -> read$_A$(level[B]) Wątek $B$ znajduje się na poziomie $j$, za każdym razem, gdy wątek $A$ odczytuje wartość $level[B]$, otrzymuje wartość $>=j$, więc $A$ nie mógł ukończyć swojej pętli oczekiwania w linii $21$, sprzeczność ## Z2 ![](https://i.imgur.com/9gBnw3m.png) ```java= class Filter implements Lock { //informacje o poziomie każdego wątku int[] level; //informacje o wątku który jako ostatni zgłosił chęć zdobycia poziomu int[] victim; public Filter(int n) { level = new int[n]; victim = new int[n]; // use 1..n-1 for (int i = 0; i < n; i++) { level[i] = 0; } } public void lock() { int me = ThreadID.get(); // returns 0..n-1 for (int i = 1; i < n; i++) { // attempt to enter level i level[me] = i; victim[i] = me; // spin while conflicts exist // inny wątek jest na wyższym lub równym poziomie // i nasz wątek jako ostatni zgłosił chęć zdobycia poziomu while (( ∃ k != me) (level[k] >= i && victim[i] == me)) {}; } } public void unlock() { int me = ThreadID.get(); level[me] = 0; //zerujemy poziom wątku } } ``` Dowód przez infukcję odwrotną na poziomach #### Baza (poziom $n-1$): Zawiera najwyżej jeden wątek #### Krok Załóżmy, że każdy wątek, który dotrze do poziomu $>=j+1$ osiągnie sekcję krytyczną Załóżmy, że $A$ utknął na poziomie $j$. Wtedy z pierwszego założenia, na wyższych poziomach nie ma wątków. Kiedy $A$ ustawi $level[A]$ na $j$, to żaden z kolejnych wątków na poziomie $j-1$ nie będzie mógł wejść na poziom $j$, a wartości $victim$ i $level$ się już nie zmieniają. Przypadki: - Wątek $A$ jest jedynym wątkiem na poziomie $>=j$: Wtedy wejdzie na poziom $j+1$ - Załóżmy, że na poziomie $j$ nie może utknąć mniej niż $k$ wątków, niech będą to między innymi wątki $A$ i $B$: $A$ jest zablokowany tak długo, jak $victim[j]$ == $A$, $B$ tak długo jak $victim[j]$ == $B$ Jednak wartość $victim$ się nie zmienia i $A!=B$, więc jeden z nich przejdzie na poziom $j+1$, redukując liczbę zablokowanych wątków do $k-1$, sprzeczność **Powyższy dowód również pokazuje własność niezakleszczenia** ## Z3 ![](https://i.imgur.com/sC6ln7C.png) ```java= class Filter implements Lock { //informacje o poziomie każdego wątku int[] level; //informacje o wątku który jako ostatni zgłosił chęć zdobycia poziomu int[] victim; public Filter(int n) { level = new int[n]; victim = new int[n]; // use 1..n-1 for (int i = 0; i < n; i++) { level[i] = 0; } } public void lock() { int me = ThreadID.get(); // returns 0..n-1 for (int i = 1; i < n; i++) { // attempt to enter level i level[me] = i; victim[i] = me; // spin while conflicts exist // inny wątek jest na wyższym lub równym poziomie // i nasz wątek jako ostatni zgłosił chęć zdobycia poziomu while (( ∃ k != me) (level[k] >= i && victim[i] == me)) {}; } } public void unlock() { int me = ThreadID.get(); level[me] = 0; //zerujemy poziom wątku } } ``` SW - sekcja wejściowa (instrukcje przypisania `level[me]` i `victim[i]`) Załóżmy, że uruchamiamy algorytm z użyciem 3 wątków 1. Wątek 1 wchodzi do `lock` i wykonuje SW (`level[me] = 1`, `victim[1] = me`) i zostaje wywłaszczony 2. W wątku 2 to samo co w kroku 1., jest ustawiony jako `victim`, zostaje wywłaszczony 3. W wątku 3 to samo co w kroku 2., jest ustawiony jako `victim`, zostaje wywłaszczony 4. Wątek 2 zostaje kontynuowany, wchodzi na wyższy poziom (`victim[1] = 3`). Warunek czekania w pętli `while` (linijka 21) nie jest spełniony, więc może przejść przez wszystkie następne poziomy, co też mu się udaje i wchodzi do CS. Po wykonaniu znów wykonuje `lock()`. Jest ustawiony jako `victim` i zostaje wywłaszczony. 5. W wątku 3 to samo co w kroku 4. Może wystąpić sytuacja taka, że kroki 4 i 5 będą powtarzały się dowolną liczbę razy, zwiększając liczbę udanych wejść na wyższy poziom dla wątków 2 i 3, i nie zmieniając dla wątku 1, zatem ta różnica dla wątku 1 i któregoś z wątków 2 i 3 może być dowolnie duża, nie istnieje taka stała $r$, że operacja przejścia na kolejny poziom ma własność $r$-ograniczonego czekania ## Z4 chyba jakis skip material ## Z5 ![](https://i.imgur.com/hKKUT7O.png) ![](https://i.imgur.com/pZpPgXV.png) **Sekwencyjne** - nie nachodzą na siebie, zawsze następują jeden po drugim **Historia** - skończona sekwencja zdarzeń wezwania i odpowiedzi metody. Historia $H$ jest **linearyzowalna**, jeżeli może być rozszerzona do $G$ poprzez - dołączenie odpowiedzi na zero lub więcej wezwań w toku - ignorowanie pozostałych wezwań w toku $G$ jest równoważna - legalnej sekwencyjnej historii $S$ - gdzie $\rightarrow_G \subset \rightarrow_S$ 1 ![](https://i.imgur.com/QPnJUaZ.png) Historia H ``` B r.write(1) A r.read() C r.write(2) A r: 1 B r: void C r: void B r.read() B r: 2 ``` Legalna, sekwencyjna historia $S$, w której zachowujemy $\rightarrow_H$ ``` B r.write(1) B r: void A r.read() A r: 1 C r.write(2) C r: void B r.read() B r: 2 ``` $\rightarrow_H\subset\rightarrow_S$ $\rightarrow_H=$ { B write(1) -> B read(), A read() -> B read(), C write(2) -> B read() } $\rightarrow_S=$ { B write(1) -> A read() -> C write(2) -> B read() } 2 ![](https://i.imgur.com/pYb66Je.png) Historia H ``` B r.write(1) A r.read() C r.write(2) A r: 1 C r: void B r: void B r.read() B r: 1 ``` rownowazna poprawnej sekwencyjnie historii S ``` C r.write(2) C r: void B r.write(1) B r: void A r.read() A r: 1 B r.read() B r: 1 ``` $\rightarrow_H \subset \rightarrow_S$ $\rightarrow_H=$ { C write(2) -> B read(), B write(1) -> B read(), A read() -> B read() } $\rightarrow_S=$ { C write(2) -> B write(1) -> A read() -> B read() } 3 ![](https://i.imgur.com/mLB8Uu2.png) Historia H ``` A p.enq(x) A p: void B p.enq(y) B p: void A p.deq(y) A p: void ``` nie jest linearyzowalna, ponieważ brakuje `p.deq(x)` przed `p.deq(y)` (nie zgadza się FIFO) 4 ![](https://i.imgur.com/9OvJ8gG.png) Historia H ``` A p.enq(x) A p: void B q.enq(y) B q: void A q.enq(x) A q: void B p.enq(y) B p: void A p.deq(y) A p: void B q.deq(x) B q: void ``` czyli p: x -> yx -> x q: y -> xy -> y ponownie historia nie jest linearyzowalna, ponieważ ani p ani q nie otrzymaliśmy efektów oczekiwanych od kolejek FIFO między A: p.deq(y) i udanym zapisie x w A: p.enq(x) brakuje p.deq(x) między B: q.deq(x) i udanym zapisie y w B: q.enq(y) brakuje q.deq(y) ## Z6 ![](https://i.imgur.com/TIE7laL.png) ![](https://i.imgur.com/BpI82IW.png) ![](https://i.imgur.com/kfml2sM.png) 1 ![](https://i.imgur.com/RE8pGQb.png) Jeżeli historia jest linearyzowalna to jest sekwencyjnie spójna. B r.write(1) -> A r.read(1) -> C r.write(2) -> B r.read(2) 2 ![](https://i.imgur.com/BpuJbKS.png) Jeżeli historia jest linearyzowalna to jest sekwencyjnie spójna. C r.write(2) -> B r.write(1) -> A r.read(1) -> B r.read(1) 3 ![](https://i.imgur.com/y6fEGR9.png) G ``` A p.enq(x) A p: void B p.enq(y) B p: void A p.deq(y) A p: void ``` S ``` B p.enq(y) B p: void A p.enq(x) A p: void A p.deq() A p: y ``` G jest ekwiwalentne do S B p.enq(y) -> A p.enq(x) -> A p.deq(y) 4 ![](https://i.imgur.com/jeqlnwH.png) G ``` A p.enq(x) A p: void B q.enq(y) B q: void A q.enq(x) A q: void B p.enq(y) B p: void A p.deq(y) A p: void B q.deq(x) B q: void ``` Aby zachować własność FIFO muszą być spełnione warunki: - p: B p.enq(y) ==1== -> A p.enq(x) ==2== - q: A q.enq(x) ==3== -> B q.enq(y) ==4== Aby zachować ekwiwalentność musi być: A p.enq(x) ==2== -> A q.enq(x) ==3== B q.enq(y) ==4== -> B p.enq(y) ==1== sprzeczność, diagram nie przedstawia sekwencyjnie spójnych historii