Programowanie współbieżne Rozwiązywanie problemu wzajemnego wykluczania. Rafał Skinderowicz

Podobne dokumenty
Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Spis treści. Księgarnia PWN: Maurice Herlihy, Nir Shavit - Sztuka programowania wieloprocesorowego. Podziękowania Przedmowa...

Procesory wielordzeniowe (multiprocessor on a chip) Krzysztof Banaś, Obliczenia wysokiej wydajności.

Model pamięci. Rafał Skinderowicz

Programowanie współbieżne Wykład 5. Rafał Skinderowicz

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Przetwarzanie wielowątkowe przetwarzanie współbieżne. Krzysztof Banaś Obliczenia równoległe 1

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Procesory wielordzeniowe (multiprocessor on a chip) Krzysztof Banaś, Obliczenia wysokiej wydajności.

Lock Manager Deadlock Źródła Jak starczy czasu. Dreadlocks. Konrad Błachnio MIMUW 19 maja 2010

procesów Współbieżność i synchronizacja procesów Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

Architektura komputerów

Współbieżność w środowisku Java

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Ograniczenia efektywności systemu pamięci

Programowanie współbieżne Wykład 2. Rafał Skinderowicz

Model pamięci. Rafał Skinderowicz

Wprowadzenie do programowania współbieżnego

Synchronizacja procesów i wątków

1 Wątki 1. 2 Tworzenie wątków 1. 3 Synchronizacja 3. 4 Dodatki 3. 5 Algorytmy sortowania 4

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Obliczenia równoległe i rozproszone w JAVIE. Michał Kozłowski 30 listopada 2003

Dr inż. hab. Siergiej Fialko, IF-PK,

WSPÓŁBIEŻNOŚĆ. MATERIAŁY:

Programowanie współbieżne Rozwiązywanie problemu wzajemnego wykluczania. Rafał Skinderowicz

Programowanie obiektowe

Programowanie współbieżne Laboratorium nr 12

Generatory. Michał R. Przybyłek

Ograniczenia efektywności systemu pamięci

Zrównoleglenie i przetwarzanie potokowe

Monitory. Jarosław Kuchta

Współbieżność w Javie

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

Wzorce dystrybucji i wspólbieżności autonomicznej

4. Procesy pojęcia podstawowe

Bazy danych. Andrzej Łachwa, UJ, /15

Programowanie współbieżne Wykład 2. Iwona Kochańska

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

9. Problem wzajemnego wykluczania i sekcji krytycznej

Język Java wątki (streszczenie)

Współbieżność w Javie

Architektura komputerów

Programowanie komputerów

Programowanie współbieżne Wykład 9 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

System pamięci. Pamięć wirtualna

Mechanizmy pracy równoległej. Jarosław Kuchta

5. Model komunikujących się procesów, komunikaty

Programowanie współbieżne i rozproszone

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

Programowanie współbieżne Wykład 7. Iwona Kochaoska

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Systemy operacyjne. Zajęcia 11. Monitory

Wielowątkowość mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Przeplot. Synchronizacja procesów. Cel i metody synchronizacji procesów. Wątki współbieżne

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

4. Procesy pojęcia podstawowe

Przetwarzanie potokowe pipelining

Java: interfejsy i klasy wewnętrzne

Aplikacje w Javie- wykład 11 Wątki-podstawy

Wykład 4. Synchronizacja procesów (i wątków) cześć I. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wydajność systemów a organizacja pamięci, czyli dlaczego jednak nie jest aż tak źle. Krzysztof Banaś, Obliczenia wysokiej wydajności.

Program jest więc strukturą statyczną zapisaną na jakimś nośniku. Natomiast proces jest wykonującym się programem.

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

Porządek dostępu do zasobu: procesory obszary pamięci cykle procesora pliki urządzenia we/wy

Dokumentacja do API Javy.

Architektura mikroprocesorów TEO 2009/2010

Podstawy Informatyki DMA - Układ bezpośredniego dostępu do pamięci

Wstęp do programowania 2

Kurs programowania. Wykład 8. Wojciech Macyna

Rys. 1. Podłączenie cache do procesora.

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Architektura Systemów Komputerowych

Algorytmy dla maszyny PRAM

Java. Programowanie Obiektowe Mateusz Cicheński

Multiprocessor Shared-Memory Information Exchange. Damian Klata, Adam Bułak

Transakcje. (c) Instytut Informatyki Politechniki Poznańskiej

Organizacja pamięci współczesnych systemów komputerowych : pojedynczy procesor wielopoziomowa pamięć podręczna pamięć wirtualna

Systemy Operacyjne synchronizacja i komunikacja procesów

Podstawy współbieżności

Układ sterowania, magistrale i organizacja pamięci. Dariusz Chaberski

Klasyczne problemy współbieżności. Problem producenta i konsumenta Problem czytelników i pisarzy Problem pięciu filozofów

Architektura komputerów

Programowanie współbieżne Wykład 10 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

Zaawansowane programowanie w C++ (PCP)

METODY ELIMINACJI STUDENTÓW INFORMATYKI. Czyli co student INF-EKA powinien wiedzieć o MESI...

System pamięci. Pamięć wirtualna

ξ II.UWr Wprowadzenie do STM

LEKCJA TEMAT: Zasada działania komputera.

Programowanie wielowątkowe. Tomasz Borzyszkowski

Wykład 13. Systemy wieloprocesorowe. Wojciech Kwedlo Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Lista, Stos, Kolejka, Tablica Asocjacyjna

Systemy operacyjne III

Języki i Techniki Programowania II. Wykład 7. Współbieżność 1

Programowanie Niskopoziomowe

Wątki w Javie. Piotr Tokarski

Transkrypt:

Programowanie współbieżne Rozwiązywanie problemu wzajemnego wykluczania Rafał Skinderowicz

Wady protokołów WW z zastosowaniem atomowych rejestrów RW Rozwiązywanie problemu wzajemnego wykluczania za pomocą rozwiązań programowych bazujących wyłącznie na atomowych instrukcjach odczytu i zapisu: jest stosunkowo wolne wymaga dodatkowej pamięci o rozmiarze liniowo rosnącym wraz z liczbą procesorów 2

Alternatywa Lepszą alternatywą jest zastosowanie silniejszych typów rejestrów typu odczyt-modyfikacja-zapis (ang. read-modify-write, RMW) Implementowane za pomocą specjalnych rozkazów procesora możemy mówić o nich jako rozwiązaniach sprzętowo programowych 3

getandset() Pseudokod implementacji operacji getandset(v) 1 public abstract class RMWRegister { 2 private int value; 3 4 public int synchronized getandset(int v) { 5 int prior = this.value; 6 this.value = v; // Nowa wartość rejestru 7 return prior; // Zwróć poprzednią 8 } 9 } 4

getandset() Pseudokod implementacji operacji getandset(v) 1 public abstract class RMWRegister { 2 private int value; 3 4 public int synchronized getandset(int v) { 5 int prior = this.value; 6 this.value = v; // Nowa wartość rejestru 7 return prior; // Zwróć poprzednią 8 } 9 } Jak wspomniano, w praktyce realizowane za pomocą pojedynczych rozkazów procesora 4

getandincrement() 1 public abstract class RMWRegister { 2 private int value; 3 4 public int synchronized getandincrement() { 5 int prior = this.value; 6 this.value = this.value + 1; // Nowa wartość 7 return prior; // Zwróć poprzednią 8 } 9 } 5

getandadd() 1 public abstract class RMWRegister { 2 private int value; 3 4 public int synchronized getandadd(int v) { 5 int prior = this.value; 6 this.value = this.value + v; // Nowa wartość 7 return prior; // Zwróć poprzednią 8 } 9 } 6

compareandset() 1 public abstract class RMWRegister { 2 private int value; 3 public boolean synchronized compareandset( 4 int expected, int update) { 5 int prior = this.value; 6 if (this.value == expected) { 7 this.value = update; 8 return true; 9 } // else 10 return false; 11 } 12 } expected oczekiwana bieżąca wartość update nowa wartość We współczesnych procesorach (AMD, Intel) odpowiednikiem jest instrukcja porównaj-i-wymień (ang. compare-and-swap), rozkaz CMPXCHG 7

Różnice Przedstawione operacje RMW są przydatne do implementacji: algorytmów WW niewstrzymywanych (ang. lock-free) i nieczekających (ang. wait-free) struktur danych Różnią się mocą stopniem przydatności 8

Różnice Przedstawione operacje RMW są przydatne do implementacji: algorytmów WW niewstrzymywanych (ang. lock-free) i nieczekających (ang. wait-free) struktur danych Różnią się mocą stopniem przydatności Jako miarę mocy operacji RMW można użyć liczbę konsensusu 8

Problem konsensusu 32 19 21 Rysunek 1: Na podstawie: Art of Multiprocessor Programming, M. Herlihy i N. Shavit (CC BY-SA 3.0) Każdy wątek ma swoją wartość, którą chce wpisać do rejestru. 9

Problem konsensusu 19 19 19 Wątki komunikują się... I godzą się na jedną z zaproponowanych wartości 10

Konsensus formalnie W problemie konsensusu wątki (procesy) podejmują decyzję, która jest: spójna wszystkie wątki godzą się na tę samą wartość poprawna ustalona wartość jest wartością zaproponowaną przez któryś z wątków (a nie np. połączeniem wartości) 11

Protokół konsensusu Interesuje nas rozwiązanie problemu konsensusu, które jest nieczekające (ang. wait-free), tj. każde wykonanie wymaga skończonej liczby kroków nie może się dowolnie przedłużać, bez WW Rozwiązanie takie nazywane jest protokołem konsensusu Protokół konsensusu implementowany jest za pomocą obiektów konsensusu 12

Obiekt konsensusu Protokół konsensusu jest realizowany przez obiekt konsensusu 1 public interface Consensus<T> { 2 T decide(t value); 3 } 4 abstract class ConsensusProtocol<T> 5 implements Consensus<T>{ 6 protected T[] proposed = new T[N]; // tab. na proponowane wartości 7 protected void propose(t value) { 8 proposed[threadid.get()] = value; 9 } 10 abstract public T decide(t value); 11 } 13

Liczba konsensusu Mówimy, że klasa obiektów konsensusu ma liczbę konsensusu równą n, jeżeli pozwala na rozwiązanie problemu konsensusu dla n wątków Przykładowo, klasa obiektów o liczbie konsensusu równej 2 pozwala na rozwiązanie problemu konsensusu dla pary wątków 14

Liczba konsensusu Tw. Liczba konsensusu rejestrów atomowych typu odczytzapis (RW) Nie istnieje nieczekająca implementacja protokołu konsensusu dla n wątków, n > 1, za pomocą atomowych rejestrów RW 15

Liczba konsensusu Tw. Liczba konsensusu rejestrów atomowych typu odczytzapis (RW) Nie istnieje nieczekająca implementacja protokołu konsensusu dla n wątków, n > 1, za pomocą atomowych rejestrów RW Czyli, korzystanie tylko z atomowych operacji odczytu i zapisu zmiennych (rejestrów) nie wystarczy do implementacji nieczekającego protokołu konsensusu, nawet dla pary wątków Musielibyśmy zastosować algorytm z oczekiwaniem (zawierający pętle itp.) Mocniejsze operacje (sprzętowe) z większą liczbą konsensusu są koniecznością, aby możliwa była implementacja nieczekających struktur danych 15

Przykładowe liczby konsensusu 1: rejestry RW 2:... kolejka FIFO rejestry RMW tylko z operacjami getandset(), getandincrement(), getandadd() : rejestr RMW z operacją compareandset() (CAS) 16

Problem konsensusu za pomocą CAS 1 AtomicLong czyjakolej = new AtomicLong(0); 2... 3 long id = Thread.currentThread().getId(); // > 0 4 // każdy wątek "proponuje" swój identyfikator 5 if (czyjakolej.compareandset(0, id) == true) { 6 System.out.println("Wygrałem"); 7 } 17

Powrót do problemu WW Rejestry RMW z liczbami konsensusu większymi od 1 można zastosować do rozwiązania problemu WW Przykładem takiej operacji jest testandset() 1 public abstract class RMWRegister { 2 private boolean value; 3 4 public int synchronized testandset(boolean v) { 5 boolean prior = this.value; 6 this.value = v; // Nowa wartość rejestru 7 return prior; // Zwróć poprzednią 8 } 9 } 18

Blokada TAS Przykładowa implementacja blokady Tast-and-set (TAS) w języku Java: 1 public class TASLock implements Lock { 2 AtomicBoolean taken = new AtomicBoolean(false); 3 public void lock() { 4 while( taken.testandset(true) ) { } 5 } 6 public void unlock() { 7 taken.set(false); 8 } 9 } 19

Blokada TAS Przykładowa implementacja blokady Tast-and-set (TAS) w języku Java: 1 public class TASLock implements Lock { 2 AtomicBoolean taken = new AtomicBoolean(false); 3 public void lock() { 4 while( taken.testandset(true) ) { } 5 } 6 public void unlock() { 7 taken.set(false); 8 } 9 } taken.testandset(true) wykonuje się natychmiastowo dla dowolnej liczby wątków oczywiście, sam algorytm WW wymaga czekania, ponieważ dwa wątki nie mogą jednocześnie wykonywać sekcji krytycznej Zalety: Prosta w implementacji Tylko 1 rejestr RMW ( AtomicBoolean taken ) 19

A co z wydajnością? czas idealna blokada liczba wątków Chcielibyśmy mieć taką blokadę, aby czas wykonywania protokołów wstępnego i końcowego nie zależał od liczby wątków brak dodatkowego narzutu czasowego oprócz, oczywiście, koniecznego oczekiwania na zwolnienie sekcji krytycznej przez inny wątek 20

W praktyce blokada TASLock czas liczba wątków 21

Wersja 2. TTASLock Wersja Test-and-test-and-set 1 class TTASlock { 2 AtomicBoolean taken = new AtomicBoolean(false); 3 4 void lock() { 5 while (true) { 6 while (taken.get()) { 7 } 8 if (taken.getandset(true) == false) { 9 return ; 10 } 11 } 12 } 13... 14 } 22

Wersja 2. TTASLock Wersja Test-and-test-and-set 1 class TTASlock { 2 AtomicBoolean taken = new AtomicBoolean(false); 3 4 void lock() { 5 while (true) { 6 while (taken.get()) { 7 } 8 if (taken.getandset(true) == false) { 9 return ; 10 } 11 } 12 } 13... 14 } Jak widać kod bardziej złożony, niż blokady TASLock Obie są poprawne i są logicznie równoważne 22

W praktyce blokada TASLock blokada TTASLock czas liczba wątków 23

W praktyce blokada TASLock blokada TTASLock czas liczba wątków Różnica w wydajności algorytmów wynika z różnego sposobu wykorzystania pamięci podręcznej (ang. cache memory) 23

Organizacja dostępu do pamięci cache cache magistrala cache pamięć (RAM) We współczesnych komputerach procesory nie odczytują / zapisują danych bezpośrednio do pamięci głównej Odczyt / zapis za pośrednictwem pamięci podręcznych (ang. cache memory) W praktyce pamięć podręczna może mieć kilka poziomów (L1, L2, L3) 24

Opóźnienia Tabela 1: Przykładowe czasy wykonania różnych operacji wg Jeffa Deana Operacja Odczyt słowa z pamięci L1 Odczyt słowa z pamięci L2 Odczyt z pamięci głównej Założenie/zwolnienie blokady (ang. mutex lock/unlock) Przesłanie 2KB po sieci 1Gbps Sekwencyjny odczyt 1 MB z pamięci Ustawienie głowicy HDD Sekwencyjny odczyt 1 MB z HDD Czas 0.5 ns 7 ns 100 ns 100 ns 20,000 ns 250,000 ns 10,000,000 ns 30,000,000 ns Czasy te będą różne dla różnych modeli procesorów, zegarów taktowania itp. chodzi tylko o rząd wielkości 25

Dostęp do pamięci Jeżeli procesor próbuje odczytać wartość danej, to najpierw sprawdza, czy dana ta znajduje się w pamięci podręcznej tak, to nie ma potrzeby odwoływania się do pamięci wyższego poziomu trafienie (ang. cache hit) nie nastąpiło chybienie (ang. cache miss) dana musi zostać pobrana z pamięci głównej Ten schemat jest uproszczony w stosunku do realnych architektur W przypadku architektur wieloprocesorowych (wielordzeniowych) sytuacja się komplikuje 26

Spójność pamięci podręcznej Dane mogą znajdować się zarówno w pamięci głównej, jak i pamięciach podręcznych różnych procesorów Proc. 1. ma kopię danych dane cache Bus cache memory dane Konieczne jest zapewnienie spójności pamięci podręcznych (ang. cache coherence) 27

Protokół MESI Jeden z prostszych, ale praktycznych protokołów zapewnienia spójności pamięci podręcznych Z każdym wierszem pamięci podręcznej związane są dodatkowe bity stanu: Modified wiersz pamięci został zmodyfikowany i musi zostać zapisany do pam. głównej Exclusive wiersz nie jest zmodyfikowany i żaden inny procesor go nie buforuje Shared wiersz nie jest zmodyfikowany, ale znajduje się w kilku pamięciach podręcznych Invalid wiersz nie zawiera istotnych danych 28

MESI A B C a E a Procesor A odczytuje dane spod adresu a i zapisuje w swojej pamięci podręcznej w stanie wyłączności (ang. exclusive, E) 29

MESI A B C a S a S a Procesor B żąda danych spod adresu a Kontroler pamięci podręcznej proc. A wykrywa żądanie i odpowiada aktualną wartością danych Obie kopie danych są w stanie współdzielenia (ang. shared, S) 30

MESI A B C a xa I M a Procesor B modyfikuje dane spod adresu a Rozgłasza do pozostałych informację, że dokonał modyfikacji jego kopia danych znajduje się w stanie zmodyfikowanym (ang. modified, M) Pozostałe procesory unieważniają swoje kopie (ang. invalid, I) 31

MESI A B C xa xa S S xa Próba odczytu danych przez proc. A wymusza wysłanie przez procesor B aktualnej wartości danej do pozostałych procesorów oraz pamięci głównej Kopie znajdują się ponownie w stanie współdzielonym 32

Opóźnienie zapisu Modyfikacja zmiennej nie wymusza natychmiastowego wysłania aktualnej wartości do pozostałych procesorów i pamięci podręcznej Efektywniejsze jest buforowanie zapisu, tak by zapisywać większe porcje danych Buforowanie zapisu nie psuje spójności pamięci Nie ma to nic wspólnego ze słowem volatile w Javie Procesory korzystają z jednej współdzielonej szyny pamięci tylko jeden przesyła dane w danym momencie potencjalne wąskie gardło 33

TASLock a TTASLock 1 public class TASLock implements Lock { 2 public void lock() { 3 while( taken.testandset(true ) ) { } 4 } 5 } 1 class TTASlock { 2 void lock() { 3 while (true) { 4 while (taken.get()) { 5 } 6 if (taken.getandset(true ) == false) { 7 return ; 8 } 9 } 10 } 11 } 34

TASLock a TTASLock 1 public class TASLock implements Lock { 2 public void lock() { 3 while( taken.testandset(true ) ) { } 4 } 5 } Wątki oczekując unieważniają za każdym razem wartość zmiennej taken Wymaga dostępu do szyny pamięci, by rozgłosić modyfikację zmiennej do pozostałych procesorów / rdzeni Wątek, który chce zwolnić blokadę opóźniany przez wątki czekające 1 class TTASlock { 2 void lock() { 3 while (true) { 4 while (taken.get()) { 5 } 6 if (taken.getandset(true ) == false) { 7 return ; 8 } 9 } 10 } 11 } 35

TASLock a TTASLock 1 public class TASLock implements Lock { 2 public void lock() { 3 while( taken.testandset(true ) ) { } 4 } 5 } Wątki oczekując unieważniają za każdym razem wartość zmiennej taken Wymaga dostępu do szyny pamięci, by rozgłosić modyfikację zmiennej do pozostałych procesorów / rdzeni Wątek, który chce zwolnić blokadę opóźniany przez wątki czekające 1 class TTASlock { 2 void lock() { 3 while (true) { 4 while (taken.get()) { 5 } 6 if (taken.getandset(true ) == false) { 7 return ; 8 } 9 } 10 } 11 } Wątki oczekujące nie korzystają ze współdzielonej szyny pamięci mają aktualną wartość zmiennej taken w pamięci podręcznej Zwolnienie blokady unieważnienie wszystkich kopii 35

Wady protokołów z aktywnym oczekiwaniem Przedstawione dotychczas algorytmy wzajemnego wykluczania implementują aktywne oczekiwanie nieustanne sprawdzanie warunku w pętli while Blokady implementowane za pomocą tych algorytmów nazywane są blokadami wirującymi (ang. spin lock) Wątek zużywa czas procesora nie wykonując żadnych produktywnych operacji 36

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? 37

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? To zależy: 37

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? To zależy: Jeżeli wątków jest mniej, niż procesorów i każdy wykonuje się na odrębnym procesorze, to możemy pozwolić sobie na stratę Jeżeli blokada zakładana jest na bardzo krótki czas i tylko niewielka liczba wątków chce ją założyć (niska rywalizacja), to blokady wirujące mogą być dobrym pomysłem 37

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? To zależy: Jeżeli wątków jest mniej, niż procesorów i każdy wykonuje się na odrębnym procesorze, to możemy pozwolić sobie na stratę Jeżeli blokada zakładana jest na bardzo krótki czas i tylko niewielka liczba wątków chce ją założyć (niska rywalizacja), to blokady wirujące mogą być dobrym pomysłem Alternatywa uśpienie wątku oczekującego na zwolnienie blokady 37

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? To zależy: Jeżeli wątków jest mniej, niż procesorów i każdy wykonuje się na odrębnym procesorze, to możemy pozwolić sobie na stratę Jeżeli blokada zakładana jest na bardzo krótki czas i tylko niewielka liczba wątków chce ją założyć (niska rywalizacja), to blokady wirujące mogą być dobrym pomysłem Alternatywa uśpienie wątku oczekującego na zwolnienie blokady Trzeba wziąć pod uwagę czas przełączania kontekstu z uśpionego wątku na inny 37

Wady protokołów z aktywnym oczekiwaniem Czy powinno się unikać blokad z aktywnym oczekiwaniem? To zależy: Jeżeli wątków jest mniej, niż procesorów i każdy wykonuje się na odrębnym procesorze, to możemy pozwolić sobie na stratę Jeżeli blokada zakładana jest na bardzo krótki czas i tylko niewielka liczba wątków chce ją założyć (niska rywalizacja), to blokady wirujące mogą być dobrym pomysłem Alternatywa uśpienie wątku oczekującego na zwolnienie blokady Trzeba wziąć pod uwagę czas przełączania kontekstu z uśpionego wątku na inny Rozwiązania hybrydowe chwila aktywnego oczekiwania, potem uśpienie 37

Protokół z wycofywaniem W alg. TTASLock może się zdarzyć, że wątek zobaczy wartość zmiennej taken = false, a więc może założyć blokadę Ale próba założenia kończy się porażką 38

Protokół z wycofywaniem W alg. TTASLock może się zdarzyć, że wątek zobaczy wartość zmiennej taken = false, a więc może założyć blokadę Ale próba założenia kończy się porażką Oznacza, to że inny wątek (rywal) go ubiegł Zjawisko dużej rywalizacji (ang. contention) jest niekorzystne 38

Protokół z wycofywaniem W alg. TTASLock może się zdarzyć, że wątek zobaczy wartość zmiennej taken = false, a więc może założyć blokadę Ale próba założenia kończy się porażką Oznacza, to że inny wątek (rywal) go ubiegł Zjawisko dużej rywalizacji (ang. contention) jest niekorzystne Rozwiązanie wątek jest usypiany na pewien czas, dając szansę pozostałym wątkom na założenie blokady i zmniejszając tym samym rywalizację 38

Protokół z wycofywaniem Dokładniej, wątek usypiany jest na okres czasu o losowej długości Ponawia próbę założenia blokady W przypadku porażki okres czasu jest wydłużany dwukrotnie (ale, do pewnych granic) Algorytm taki nazywany jest blokadą z wykładniczym wycofywaniem (ang. exponential backoff ) 39

Protokół z wycofywaniem 1 public class BackoffLock implements Lock { 2... 3 public void lock() { 4 int delay = MIN_DELAY; 5 while (true) { 6 while (taken.get()) { 7 } 8 if (!lock.getandset(true)) 9 return; 10 Thread.sleep(random() % delay); 11 if (delay < MAX_DELAY) { 12 delay = 2 * delay; 13 } 14 } 15 } 16 } 40

Protokół z wycofywaniem uwagi Lepsza wydajność, niż poprzednich blokad w przypadku dużej rywalizacji Łatwa implementacja Problem jak dobrać wartości stałych MIN_DELAY i MAX_DELAY W praktyce stosowane są bardziej złożone algorytmy blokad: kolejkowe hierarchiczne kompozycyjne hybrydowe 41

Algorytmy nieczekające Algorytm nieczekający Algorytm nazywamy nieczekającym (ang. wait-free), jeżeli każde jego wykonanie przez współbieżne wątki zostanie ukończone w skończonej liczbie kroków 42

Algorytmy nieczekające Algorytm nieczekający Algorytm nazywamy nieczekającym (ang. wait-free), jeżeli każde jego wykonanie przez współbieżne wątki zostanie ukończone w skończonej liczbie kroków Jest to silniejsza własność niż niewstrzymywanie Gwarantuje, że każdy wątek robi postępy w obliczeniach brak zakleszczenia i zagłodzenia Wada trudne w implementacji i niestety często mniej efektywne niż alg. niewstrzymywane 42

Algorytmy niewstrzymywane Algorytm niewstrzymywany Algorytm jest niewstrzymywany (ang. lock-free ), jeżeli gwarantuje, że nieskończenie często jakieś jego wykonanie zostanie ukończone w skończonej liczbie kroków 43

Algorytmy niewstrzymywane Algorytm niewstrzymywany Algorytm jest niewstrzymywany (ang. lock-free ), jeżeli gwarantuje, że nieskończenie często jakieś jego wykonanie zostanie ukończone w skończonej liczbie kroków Dzięki alg. niewstrzymywanemu system jako całość wykonuje kolejne obliczenia, choć być może, nie wszystkie wątki liczą Innymi słowy, alg. niewstrzymywany nie musi być sprawiedliwy i któreś z wątków mogą być głodzone kosztem pozostałych Wyklucza możliwość zakleszczenia Algorytmy nieczekające są niewstrzymywane, ale nie odwrotnie 43

Algorytmy niehamowane Algorytm niehamowany Algorytm nazywamy niehamowanym (ang. obstruction free ), jeżeli w dowolnym momencie po rozpoczęciu jego wykonywania w izolacji zostanie zakończona w skończonej liczbie kroków. 44

Algorytmy niehamowane Algorytm niehamowany Algorytm nazywamy niehamowanym (ang. obstruction free ), jeżeli w dowolnym momencie po rozpoczęciu jego wykonywania w izolacji zostanie zakończona w skończonej liczbie kroków. Innymi słowy, jeżeli pozostałe wątki zostaną zatrzymane, to dany wątek zakończy wykonywanie w skończonej liczbie kroków Wyklucza użycie blokad, ale nie gwarantuje postępu obliczeń Metody optymistycznej kontroli współbieżności są zazwyczaj niehamowane wykrycie konfliktu z innymi wątkami powoduje, że wątek się wycofuje Wszystkie algorytmy nieczekające i niewstrzymywane są również niehamowane, ale nie odwrotnie 44

Mechanizmy zamków (blokad) w Javie Pakiet java.util.concurrent.locks Interfejs Lock Implementacje: ReentrantLock, ReentrantReadWriteLock Konieczność stosowania tych samych zamków dla tych samych sekcji krytycznych i w tej samej kolejności Sekcje krytyczne niepowiązane ze sobą (zmienne nie zależą od zmiennych w innej sekcji bezpośrednio i pośrednio) mogą być realizowane współbieżnie jeżeli będą chronione odrębnymi zamkami 45

Lock c.d. Interfejs Lock udostępnia również metodę trylock(), która próbuje uzyskać zamek, ale jeżeli się to nie uda, to nie wstrzymuje wykonania wątku 1 Lock lock =...; 2 if (lock.trylock()) { 3 try { 4 // sekcja krytyczna 5 } finally { 6 lock.unlock(); 7 } 8 } else { 9 // alternatywne operacje, sekcja lokalna 10 } 46

ReentrantLock Podstawowa implementacja interfejsu Lock Wydajność podobna do bloków / metod synchronizowanych za pomocą konstrukcji synchronized Wątek, który założył blokadę może ją założyć ponownie Dzięki konstruktorowi ReentrantLock(boolean fair) można stworzyć blokadę z gwarancją braku zagłodzenia i obsługą oczekujących wątków zgodnie z kolejnością zgłaszania żądań Można sprawdzić, czy i jakie wątki czekają na zwolnienie blokady 47

Interfejs ReadWriteLock ReadWriteLock zawiera parę skojarzonych blokad: jedna tylko dla operacji odczytu druga dla operacji odczytu i zapisu Przydatny, gdy dane współdzielone są często odczytywane, ale rzadko modyfikowane wątki, które tylko czytają zakładają blokadę do odczytu wątki modyfikujące zakładają blokadę do zapisu Implementacja jest bardziej złożona, niż w przypadku ReentrantLock trzeba sprawdzić wydajność w praktyce 48

Interfejs ReadWriteLock przykład 1 class CachedData { 2 Object data; 3 volatile boolean cachevalid; 4 ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 5 void processcacheddata() { 6 rwl.readlock().lock(); 7 if (!cachevalid) { 8 rwl.readlock().unlock(); // zwolnij blokadę do odczytu 9 rwl.writelock().lock(); // załóż do zapisu 10 if (!cachevalid) { // sprawdź ponownie 11 data =... 12 cachevalid = true; 13 } 14 rwl.readlock().lock(); // załóż blokadę do odczytu 15 rwl.writelock().unlock(); // zwolnij blokadę do zapisu 16 } 17 use(data); // chronione tylko blokadą do odczytu 18 rwl.readlock().unlock(); 19 } 20 } 49