###### tags: `prir` `studia` # Algorytmy na wzajemne wykluczanie ## Algorytm Dekkera - algorytm wyraża chęć wejścia do sekcji krytycznej - jeśli dwa algorytmy wyraziły chęć, to sprawdzają, czyja jest kolejność i jeden ustępuje drugiemu ``` boolean chcep=false, chceq=false; int czyja_kolej=1 -> PROCES P loop forever { sekcja_lokalna chcep=true while (chceq) { if (czyja_kolej==2) { chcep = false await czyja_kolej == 1 chcep = true } } sekcja_krytyczna czyja_kolej = 2 chcep = false } -> PROCES Q loop forever { sekcja_lokalna chceq=true while (chcep) { if (czyja_kolej==1) { chceq = false await czyja_kolej == 2 chceq = true } } sekcja_krytyczna czyja_kolej = 1 chceq = false } ``` ## Algorytm Petersona - procesy wyrażają chęć wejścia do sekcji krytycznej - procesy zgłaszają, że poprosili o dostęp jako ostatni - jeżeli dwa procesy wyrażają chęć na wejście do sekcji krytycznej, to przechodzi ten, który nie był ostatni ``` boolean chcep=false, chceq=false int kto_ostatni=1 -> PROCES P sekcja_lokalna chcep = true kto_ostatni = 1 await chceq==false or kto_ostatni==2 sekcja_krytyczna chcep=false -> PROCES Q sekcja_lokalna chceq = true kto_ostatni = 2 await chcep==false or kto_ostatni==1 sekcja_krytyczna chceq=false ``` ## Algorytm Dorana-Thomasa - procesy zgłaszają chęć wejścia do sekcji krytycznej - ustalona jest kolej - jeśli jest kolej innego procesu, to odpuszczają i go puszczają pierwszego ``` boolean chcep=false, chceq=false int czyja_kolej=1 -> PROCES P sekcja_lokalna chcep=true if (chceq) { if(czyja_kolej==2) { chcep=false await czyja_kolej==1 chcep=true } await chceq==false } sekcja_krytyczna chcep=false czyja_kolej=2 -> PROCES Q sekcja_lokalna chceq=true if(chcep) { if(czyja_kolej==1) { chceq=false await czyja_kolej==1 chceq=true } await chcep==false } sekcja_krytyczna chceq=false czyja_kolej=1 ``` ## Algorytm piekarniany - proces który chce wejść do sekcji krytycznej pobiera bilet z numerem - bilet musi mieć wartość większą niż wartości na wczesniej wydanych biletach - jeśli proces posiada bilet z najmniejszym numerem - może wejść do sekcji krytycznej - jeżeli zdarzy się, że numery na biletach dwóch procesów beda takie same - pierwszeństwo ma ten z mniejszym identyfikatorem ### dla dwóch procesów ``` int np=0, nq=0 -> PROCES P loop forever { sekcja_lokalna np = nq+1 await nq == 0 or np <= nq sekcja_krytyczna np = 0 } -> PROCES Q loop forever { sekcja_lokalna nq = np+1 await np == 0 or nq <= np sekcja_krytyczna nq = 0 } ``` ### dla N procesów ``` int numer[0..N-1] = [0,..,0] -> PROCES [id] sekcja lokalna numer[id] = max(numer)+1 // ta petla uruchomi await czekający, aż będzie kolej // procesu id badając jego zależności z wszystkimi innymi for all other processes j { await (numer[j]==0) or (numer[id] < numer[j]) or (numer[id] == numer[j] and (id<j)) sekcja_krytyczna numer[id] = 0 } ``` ### dla N procesów bez atomowych przypisań ``` boolean wybiera[0..N-1] = [false,..,false] int numer[0..N-1] = [0,..,0] -> PROCES [id] loop forever { sekcja_lokalna wybiera[id] = true numer[id] = max(numer)+1 wybiera[id] = false for all other processes j { await wybiera[j] == false await (numer[j] == 0) or (numer[id] < numer[j]) or (numer[id] == numer[j] and id < j) } sekcja_krytyczna numer[id] = 0 } ``` ## Algorytm szybki - algorytm piekarniany wymaga wielu pętli, nawet przy braku rywalizacji - algorytm jest **szybki**, gdy w przypadku braku rywalizacji, proces może wejść do sekcji krytycznej wykonując w protokole wstępnym i końcowym stałą i niewielką liczbę instrukcji - żadna z tych instrukcji **nie może być await** ### Szkic takiego algorytmu - sekcja chroniona jest przz dwie bramki - w przypadku braku rywalizacji, proces przejdzie przez nie w skończonej liczbie kroków - w przypadku rywalizacji, tylko jeden proces przejdzie przez obie bramki, pozostałe cofają się przed pierwszą bramkę ``` int bramka1=-1, bramka2=-1 boolean chce[0..N-1] = [false,..,false] -> PROCES [id] loop infinity { 1: sekcja_lokalna 2: bramka1=id chce[id] = true 3: if (bramka2!=-1) chce[id] = false; goto 2 bramka2=id if (bramka1 != id) { chce[id] = false for all other processes j { await chce[j] == false } if (bramka2 != id) goto 2 else chce[id] = true } sekcja_krytyczna bramka2 = -1 chce[id] = false } ``` ## Algorytm Ricarta-Agrawali (w systemie rozproszonym) - poprzednie algorytmy działały na pamięci wspólnej - algorytm opiera się na pomyśle pobierania biletów - w systemie rozproszonym nie można ich bezpośrednio porównywać, trzeba je przesłać - algorytm składa się z dwóch procesów - główny - z sekcją lokalną i krytyczną - z protokołami wstępnym i końcowym - odbiorca - przetwarza otrzymane komunikaty ``` int num=0, maxNum=0 boolean chceSK = false set<int> wstrzymane -> GŁOWNY loop forever { sekcja_lokalna chceSK = true num = maxNum+1 for all other nodes N: send(REQ, N, id, num) for all other nodes N: await(RESP, N) sekcja_krytyczna chceSK = false foreach j in wstrzymane: wstrzymane.remove(j) send(RESP, N, id) } -> ODBIORCA int nadawca, reqNum loop forever { receive(REQ, nadawca, reqNum) maxNum = max(maxNum, reqNum) if !chceSK or reqNum < num or (reqNum == num and nadawca < id): send(RESP, nadawca, id) else: wstrzymane.add(nadawca) } ```