Programowanie współbieżne Wykład 10 Synchronizacja dostępu do współdzielonych zasobów Iwona Kochańska
Mechanizm synchronizacji wątków/procesów Wykorzystanie semaforów zapobiega niedozwolonemu wykonaniu operacji na określonych danych jednocześnie przez większą liczbę procesów Przez odpowiednie wykorzystywanie semaforów można zapobiec sytuacji w której wystąpi zakleszczenie lub zagłodzenie Pojęcie semafora zostało pierwszy raz zdefiniowane przez holendra Edgara Dijkstrę
mogą być wykorzystywane tam, gdzie zasób dzielony jest na ograniczoną liczbę użytkowników. Semafor działa jak furtka kontrolująca liczbę wątków wykonujących jakiś fragment kodu. Za pomocą semaforów aplikacja może kontrolować na przykład maksymalną liczbę otwartych plików, czy utworzonych okien.
Semafor w praktyce jest liczbą całkowitą, na której możliwe są dwie operace: Wait() Signal() Zapewnienie wzajemnego wykluczania: wait(semaphore) sekcja krytczna signal(semaphore) Po zakończeniu pracy w sekcji krytycznej wątek uwalnia semafor
Praktyczna definicja semafora uogólnionego wg Ben-Ariego: Semafor jest pewną całkowitą liczbą nieujemną S. Opuszczenie (wait) semafora jest równoważne wykonaniu instrukcji: jeśli S>0 to S=S-1, w przeciwnym razie wstrzymaj działanie procesu próbującego opuścić semafor Podniesienie (signal) semafora: jeśli są procesy wstrzymane przy próbie opuszczenia semafora S to wznów jeden z nich, w przeciwnym wypadku S=S+1
Przykład definicji operacji opuszczenia (wait) i podniesienia (signal) semafora sem wait(sem){ if(sem!=0) zmniejsz sem o 1 else czekaj aż sem > 0, wtedy zmniejsz o 1 } signal(sem){ zwiększ sem o 1 jeśli jakieś procesy czekają w kolejce, wznów pierwszy z nich }
Rodzaje semaforów: Semafor binarny Może być tylko w stanie podniesienia lub opuszczenia reprezentowany przez liczbę binarną S { 0, 1} Semaforem binarnym jest mutex! Semafor uogólniony możliwe wiele stanów reprezentowany przez liczbę S { 0, 1,..., } Operacje wykonywane na semaforze są atomowe Semafor można traktować jako licznik, który: jest zmniejszany (zamykany) o 1 gdy jest zajmowany jest zwiększany o 1 gdy jest zwalniany (podnoszony)
Przykład: dostęp do strumienia wyjściowego https://austingwalters.com/multithreading-semaphores/
Semafor trzeba zainicjować wywołując operację podniesienia Semafor skojarzony z danym zasobem jest początkowo ustawiany na wartość równą liczbie dostępnych zasobów tego typu. np. semafor = 5 Proces, który żąda zasobu musi najpierw sprawdzić wartość skojarzonego z tym zasobem semafora - dodatnia wartość semafora oznacza dostępność zasobu. if (semafor > 0) dostęp do zasobów Przed rozpoczęciem korzystania z zasobu proces zmniejsza wartość semafora w sposób niepodzielny. if (semafor > 0) { } semafor--; dostęp do zasobów
Zerowa wartość semafora oznacza, że nie ma wolnych zasobów i proces musi czekać aż proces zajmujący zasób przestanie z niego korzystać i zwiększy wartość semafora zwalniając zasób. Kiedy zasób zostanie zwolniony, pozostałe procesy, które na niego czekały są o tym powiadamiane przez system.
Zmienne warunkowe Aby uniknąć aktywnego odpytywania się o spełnienie określonego warunku, lepiej jest zablokować oczekujący wątek do momentu zajścia zdarzenia (spełnienia oczekiwanego warunku). Mechanizm taki jest wykorzystany w implementacji zmiennych warunkowych (condition variables). Zmienne warunkowe używane są do powiadamiania wątków o zaistnieniu określonego warunku
Zmienne warunkowe
Zmienne warunkowe Powiadomienie o zdarzeniu może zostać zrealizowane przy pomocy dwóch metod: void notify_one() odblokowuje jeden z wątków znajdujących się w stanie oczekiwania po uprzednim wywołaniu na obiekcie zmiennej warunkowej metody wait() void notify_all() odblokowuje wszystkie wątki znajdujące się w stanie oczekiwania
Zmienne warunkowe Biblioteka standardowa C++11 dostarcza dwie implementacje zmiennych warunkowych: std::condition_variable std::condition_variable_any Obie klasy współpracują z muteksem, aby zapewnić prawidłową synchronizację wątków: conditional_variable współpracuje tylko z typem std::mutex conditional_variable_any współpracuje z dowolnym typem muteksu
Zmienne warunkowe Przykład semafory.cpp