Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Systemy rozproszone W. Bartkiewicz Wykład 9. Wprowadzenie do koordynacji programów współbieżnych.

Podobne prezentacje


Prezentacja na temat: "Systemy rozproszone W. Bartkiewicz Wykład 9. Wprowadzenie do koordynacji programów współbieżnych."— Zapis prezentacji:

1 Systemy rozproszone W. Bartkiewicz Wykład 9. Wprowadzenie do koordynacji programów współbieżnych

2 Współbieżność Jeśli w systemie współistnieje jednocześnie kilka procesów (wątków wykonania), mówimy, że są one wykonywane współbieżnie. Sytuacja ta dotyczy nie tylko jednoczesnego (równoległego) wykonywania procesów (wątków) na różnych komputerach lub procesorach, ale również naprzemiennego przeplatania porcji poszczególnych procesów (wątków) na jednym procesorze. Przetwarzanie współbieżne umożliwia zwiększenie wydajności systemu, poprzez przyśpieszenie (równoległość) wykonywania operacji oraz daje możliwość jednoczesnej obsługi wielu użytkowników. Współbieżność działania powstaje w systemie rozproszonym jako naturalny wynik osobnych działań użytkowników, niezależności zasobów i procesów usługowych. Współbieżny dostęp do zasobów wspólnie wykorzystywanych może jednak powodować konflikty i musi być synchronizowany.

3 Synchronizacja Wzajemne wykluczanie Sytuację gdy dla współbieżnie działających elementów aplikacji (wątków lub procesów) pewna operacja może być w danym momencie czasu wykonywana jedynie przez jeden z nich, nazywamy wzajemnym wykluczaniem. W przypadku gdy jakiś element wykonuje operację podlegającą wzajemnemu wykluczaniu (tzw. sekcję krytyczną), inne elementy próbujące w tym samym czasie wykonywać tę operację muszą zostać zablokowane, aż do jej zakończenia przez pierwszy z nich. Wzajemne wykluczanie zawiesza więc zasadę współbieżnego wykonywania elementów aplikacji rozproszonej, synchronizując ich dostęp do pewnych operacji. Większość zagadnień synchronizacji w programowaniu współbieżnym stanowi wariant lub kombinację złożoną z kilku problemów wzajemnego wykluczania.

4 Przeplot Podstawowym założeniem programowania współbieżnego jest brak zależności czasowych między wykonywanymi instrukcjami różnych wątków (procesów): –Moment wywłaszczenia wątku (procesu) ma w dużej mierze charakter stochastyczny, zależny od konkretnego uruchomienia programu. –Szybkość wykonania poszczególnych procesów (wątków) uruchamianych równolegle (np. na różnych maszynach) może się zmieniać (np. w zależności od parametrów wykorzystywanego sprzętu, obciążenia sieci itp.) Analizując program współbieżny nie możemy brać pod uwagę żadnego konkretnego przeplotu instrukcji różnych wątków (procesów). Program musi być badany pod kątem dowolnego przeplotu instrukcji współbieżnych (wątków) procesów. –Na przykład dla wykazania nieprawidłowego działania aplikacji współbieżnej wystarczy wskazać jeden scenariusz przeplotu, który powoduje błąd.

5 Synchronizacja Dowolny przeplot operacji wszystkich synchronizowanych elementów nie może doprowadzić do złamania zasady wzajemnego wykluczania i równoczesnego wejścia kilku z nich do sekcji krytycznej. –Mechanizmy synchronizacji muszą więc zapewniać jednoczesność operacji sprawdzania możliwości wejścia elementu do strefy krytycznej i zablokowania tej możliwości innym elementom. Obok własności bezpieczeństwa programy współbieżne muszą wykazać się respektowaniem własności żywotności (ang. liveness). –Sytuację gdy synchronizowane elementy zablokują sobie wzajemnie dostęp do strefy krytycznej i żaden z nich nie może wykonać operacji w nich zawartej nazywamy zakleszczeniem lub blokadą (ang. deadlock). –Sytuację, gdy na skutek błędnej synchronizacji pewne z elementów nie otrzymują dostępu do strefy krytycznej nazywamy wykluczeniem (ang. lockout) lub zagłodzeniem (ang. starvation).

6 Wzajemne wykluczanie – kłopoty (1) bool enter1 = true, enter2 = true; void fun1(void*) { while (1) { while ( !enter1 ) ; enter2 = false; //strefa krytyczna enter2 = true; //jakies operacje } void fun2(void*) { while (1) { while ( !enter2 ) ; enter1 = false; //strefa krytyczna enter1 = true; //jakies operacje } void main() { _beginthread(fun1, 0, NULL); _beginthread(fun2, 0, NULL); } Przykra sytuacja (ale możliwa): 1.Wątek fun1 sprawdza, że enter1 ma wartość true. 2.Wątek fun1 zostaje wywłaszczony (lub pechowa kolejność operacji równoległych). 3.Wątek fun2 sprawdza, że enter2 ma wartość true (bo fun1 nie zdążył zmienić na false) – zmienia na enter1 na false i wchodzi do sekcji krytycznej. 4.Wątek fun2 zostaje wywłaszczony. 5.Wątek fun1 nie sprawdza już enter1 (bo zrobił to wcześniej), tylko przypisuje false enter2 i rozpoczyna wykonywanie sekcji krytycznej. Oooops!! Obydwa wątki wykonują sekcję krytyczną

7 Wzajemne wykluczanie – kłopoty (2) bool enter1 = true, enter2 = true; void fun1(void*) { while (1) { enter2 = false; while ( !enter1 ) ; //strefa krytyczna enter2 = true; //jakies operacje } void fun2(void*) { while (1) { enter1 = false; while ( !enter2 ) ; //strefa krytyczna enter1 = true; //jakies operacje } void main() { _beginthread(fun1, 0, NULL); _beginthread(fun2, 0, NULL); } Nadal żle: 1.Wątek fun1 ustawia enter2 na false. 2.Wątek fun1 zostaje wywłaszczony (lub pechowa kolejność operacji równoległych). 3.Wątek fun2 ustawia enter1 na false. 4.Wątek fun2 sprawdza, że enter2 ma wartość false i jest zablokowany przez pętlę while. 5.Wątek fun2 zostaje wywłaszczony. 6.Wątek fun1 sprawdza enter2 i też jest zablokowany przez pętlę while. 7.Obydwa wątki są zablokowane i czekają na siebie. Oooops!! Zakleszczenie

8 Synchronizacja System ze wspólną pamięcią Systemy operacyjne w ramach interfejsów programistycznych oferują (działające na różnych zasadach) specjalne mechanizmy synchronizacyjne, zapewniające jednoczesność testowania warunków wejścia do strefy krytycznej i zablokowania tego dostępu innym współbieżnie działającym wątkom lub procesom. Przez jednoczesność operacji rozumiemy jej atomowy charakter, tzn. wątek lub proces wykonuje tę operację jako niepodzielną całość. Nie może być w trakcie jej wykonywania wywłaszczony, ani jej instrukcje w żaden sposób nie mogą się przeplatać.

9 Synchronizacja Dwa podstawowe podejścia do blokowania współbieżnych elementów aplikacji: –Zablokowane elementy pozostają cały czas aktywne, nieustannie sprawdzając (w pętli) możliwość wejścia do strefy krytycznej – tzw. blokada wirująca (spinning blockade). Rozwiązanie to stosowane jest znacznie rzadziej, ponieważ obciąża procesor. Daje jednak większe możliwości uniknięcia zakleszczeń. –Mechanizmy synchronizacji zawieszają (i kolejkują) zablokowane elementy, aby uniknąć obciążania procesora nieustannym sprawdzaniem przez nie warunków wejścia do strefy krytycznej. Z chwilą kiedy dostęp ten jest możliwy jeden z oczekujących elementów zostaje odblokowany przez mechanizmy synchronizacyjne. W praktyce stosowane jest zazwyczaj podejście drugie, lub kombinacja zawieszania elementu aplikacji na pewien czas w powiązaniu z blokadą wirującą albo odmierzaniem czasu trwania blokady.

10 Synchronizacja MS Windows Współbieżne procesy i wątki w Windows mogą być synchronizowane: –W trybie użytkownika (dotyczy tylko wątków jednego procesu). –Za pomocą obiektów jądra systemu operacyjnego.

11 Synchronizacja w trybie użytkownika Sekcje krytyczne Do wzajemnego wykluczania wątków tego samego procesu, korzystamy zazwyczaj ze specjalnego typu danych, sekcji krytycznej CRITICAL_SECTION. Realizacja wzajemnego wykluczania: –Deklarujemy (zazwyczaj jako zmienną globalną dostępną dla wszystkich wątków) zmienną typu CRITICAL_SECTION. –Zmienne ta musi zostać zainicjowana w programie dokładnie raz i przed próbą wejścia do strefy krytycznej przez którykolwiek z wątków, przy pomocy funkcji InitializeCriticalSection. –Wątek wchodząc do strefy krytycznej wywołuje funkcję EnterCriticalSection, wychodząc LeaveCriticalSection. CRITICAL_SECTION g_cs; void ThreadFunc1(void*) { EnterCriticalSection(&g_cs); // operacje sekcji krytycznej LeaveCriticalSection(&g_cs); return(0); } void ThreadFunc2(void*) { EnterCriticalSection(&g_cs); // operacje sekcji krytycznej LeaveCriticalSection(&g_cs); return(0); } void main() { InitializeCriticalSection(&g_cs); _beginthread(fun1, 0, NULL); _beginthread(fun2, 0, NULL); }

12 EnterCriticalSection: –Jeśli żaden wątek nie korzysta z sekcji krytycznej, funkcja ta zaznacza w polach struktury, że wywołujący wątek uzyskał dostęp i wraca, umożliwiając wątkowi kontynuowanie działania i wykonanie strefy krytycznej. –Jeśli z sekcji krytycznej korzysta wątek wywołujący, to funkcja zwiększa licznik dostępów i wraca, umożliwiając wątkowi kontynuowanie działania. Sytuacja ta może mieć miejsce jedynie wtedy gdy wątek dwukrotnie wywoła EnterCriticalSection bez wywołania LeaveCriticalSection. –Jeśli z sekcji krytycznej korzysta inny wątek, wątek wywołujący jest zawieszany w kolejce wątków oczekujących. Synchronizacja w trybie użytkownika Sekcje krytyczne

13 LeaveCriticalSection –Funkcja zmniejsza wywołującemu wątkowi licznik dostępów o 1. Jeśli jego wartość jest nadal większa od 0, funkcja nie robi nic więcej i wraca do miejsca wywołania. Tak więc aby udostępnić sekcję krytyczną wątek musi tyle samo razy wywołać LeaveCriticalSection ile razy wywołał EnterCriticalSection. –Jeśli po zmniejszeniu wartość licznika jest równa 0, funkcja sprawdza czy w kolejce nie czekają jakieś inne wątki. Jeśli tak, to w uczciwy sposób wybiera jeden z nich i wznawia jego działanie, umożliwiając wykonanie strefy krytycznej. –Jeśli w kolejce nie czeka żaden wątek, funkcja tak ustawia składowe struktury, aby wskazywały na dostępność zasobu. Synchronizacja w trybie użytkownika Sekcje krytyczne

14 Funkcje działające na sekcjach krytycznych mają charakter atomowy. Sekcje krytyczne są wydajnym narzędziem synchronizującym. –Funkcje realizujące ich operacje działają w trybie użytkownika i wątek nie musi przechodzić w tryb jądra, co kosztuje (w obie strony łącznie) około 1000 cykli procesora. –Nie dotyczy to jednak sytuacji gdy strefa krytyczna jest już zajęta i wątek musi przejść w tryb oczekiwania. Oznacza to, że wątek musi zmienić swój tryb wykonania z trybu użytkownika na tryb jądra. Sekcje krytyczne mogą być wykorzystywane wyłącznie do synchronizacji wątków tego samego procesu. Dla sekcji krytycznych nie można określić limitu czasu oczekiwania na wejście do strefy krytycznej, co może prowadzić do zakleszczeń. Synchronizacja w trybie użytkownika Sekcje krytyczne

15 Synchronizacja – obiekty jądra Alternatywą jest wykorzystanie do synchronizacji współbieżnie wykonywanych wątków obiektów jądra systemu MS Windows. Obiekty jądra są bardziej wszechstronne niż mechanizmy trybu użytkownika. –Mogą służyć do synchronizacji wątków różnych procesów. Możliwe jest również określenie limitu czasu oczekiwania. –Ich wadą jest wolniejsze działanie. Każda operacja na obiekcie jądra (również synchronizacyjna) wymaga przejścia w tryb jądra. –Wolniejsze jest również wykonanie kodu funkcji w trybie jądra. Obiekty jądra, wykorzystywane do synchronizacji, mogą być w stanie sygnalizowanym lub niesygnalizowanym. –Reguły przejścia z jednego stanu w drugi zależą od typu konkretnego obiektu.

16 Synchronizacja – obiekty jądra Wątki mogą czekać na niesygnalizowane obiekty, pozostając w stanie oczekiwania do momentu ich zasygnalizowania. –Do przejścia wątku w stan oczekiwania na zasygnalizowanie obiektu służą funkcje WaitForSingleObject oraz WaitForMultipleObjects. –Zmiana stanu sygnalizowany/niesygnalizowany zazwyczaj jest wynikiem wykonania określonej operacji zależnej od konkretnego typu obiektu. –Zarówno funkcje Wait, jak i operacje zmiany stanu obiektu sygnalizowany/niesygnalizowany mają charakter atomowy.

17 Synchronizacja – obiekty jądra Muteksy Muteksy są obiektami jądra, które pozwalają zagwarantować wątkowi wyłączność na dostęp do współdzielonego zasobu (Mutex – mutual exclusion, wzajemne wykluczanie). Muteksy mają identyczną semantykę działania jak sekcje krytyczne, tyle że są obiektami jądra, a nie trybu użytkownika, co umożliwia synchronizację różnych procesów. –Gdy wątek posiada muteks – ma wyłączny dostęp do zasobu chronionego przez ten muteks. –Gdy muteks znajduje się w posiadaniu wątku, nie może go przejąć żaden inny wątek. –Wątek będący właścicielem muteksu może go przejąć wielokrotnie, musi go w takim przypadku odpowiednią liczbę razy zwrócić. Najważniejsze operacje: –CreateMutex – utworzenie nowego muteksu lub otwarcie istniejącego –OpenMutex – otwarcie istniejącego muteksu –ReleaseMutex – zwolnienie (sygnalizacja) muteksu, udostępniające go innym wątkom.

18 Synchronizacja – obiekty jądra Muteksy Realizacja zadania wzajemnego wykluczania: –Tworzymy muteks przy pomocy funkcji CreateMutex. –Wchodząc do strefy krytycznej próbujemy objąć muteks w posiadanie np. przy pomocy funkcji WaitForSingleObject. –Kończąc strefę krytyczną zwalniamy muteks (sygnalizujemy jego obiekt) przy pomocy funkcji ReleaseMutex. –Proces uzyskujący dostęp do uchwytu obiektu musi go zamknąć przy pomocy funkcji CloseHandle. –Operacje Wait oraz ReleaseMutex charakter atomowy. HANDLE hMt; void ThreadFunc1(void*) { WaitForSingleObject(hMt, INFINITE); // operacje sekcji krytycznej ReleaseMutex(hMt); return(0); } void ThreadFunc2(void*) { WaitForSingleObject(hMt, INFINITE); // operacje sekcji krytycznej ReleaseMutex(hMt); return(0); } void main() { hMt = CreateMutex(NULL, FALSE, NULL); _beginthread(fun1, 0, NULL); _beginthread(fun2, 0, NULL); // Po zakończeniu wątków // CloseHandle(hMt); }

19 Synchronizacja – obiekty jądra Muteksy Pierwszy parametr pSA to wskaźnik do struktury atrybutów bezpieczeństwa dla tworzonego obiektu. –Opisuje ona kto utworzył obiekt, kto może go używać, a kto nie. –Zagadnienie jej definiowania dotyczy kwestii programowania sieciowego, a nie współbieżnego i jest poza zakresem tematycznym zajęć. –Użycie wartości NULL udostępnia muteks procesom tego samego użytkownika, który go utworzył. Parametr fInitialOwner określa, czy muteks ma od razu po utworzeniu zostać zajęty. Wartość FALSE oznacza, że nie i jest zasygnalizowany. HANDLE CreateMutex( PSECURITY_ATTRIBUTES pSA, // atrybuty bezpieczeństwa (u nas NULL) BOOL fInitialOwner,// czy muteks od razu zajęty PCTSTR pszName// nazwa muteksu );

20 Synchronizacja – obiekty jądra Muteksy Parametr pszName pozwala na określenie nazwy muteksu. –Jeśli pszName ma wartość NULL to muteks jest nienazwany, i jego uchwyt musi być jakoś przekazany innym synchronizowanym wątkom. Jest to łatwe dla wątków jednego procesu (używamy zmiennej globalnej). Dla wątków różnych procesów jest to (nieco) bardziej kłopotliwe, dlatego w przypadku synchronizacji procesów standardem jest nazywanie muteksów. –Jeśli muteks ma podaną nazwę, to stanowi ona jego identyfikator w systemie. Każde kolejne wywołanie (również w innych procesach) funkcji CreateMutex lub OpenMutex (rzadziej stosowana) dla obiektu o tej samej nazwie będzie udostępniało uchwyt tego samego, utworzonego już wcześniej muteksu. –Uwaga: nazwa muteksu musi być więc jednoznaczna w systemie, tzn. w danym momencie nie mogą istnieć dwa różne muteksy o tej samej nazwie. HANDLE CreateMutex( PSECURITY_ATTRIBUTES pSA, // atrybuty bezpieczeństwa (u nas NULL) BOOL fInitialOwner,// czy muteks od razu zajęty PCTSTR pszName// nazwa muteksu );

21 Synchronizacja – obiekty jądra Muteksy //Pierwszy program void main() { HANDLE hMt = CreateMutex(NULL, FALSE, SuperHiperMuteks); // instrukcje pierwszego programu // gdzieś potrzeba synchronizacji z drugim WaitForSingleObject(hMt, INFINITE); // operacje sekcji krytycznej ReleaseMutex(hMt); // wiele kolejnych instrukcji pierwszego programu CloseHandle(hMt); } //Drugi program void main() { HANDLE hMt = CreateMutex(NULL, FALSE, SuperHiperMuteks); //instrukcje drugiego programu // gdzieś potrzeba synchronizacji z pierwszym WaitForSingleObject(hMt, INFINITE); // operacje sekcji krytycznej ReleaseMutex(hMt); // wiele kolejnych instrukcji drugiego programu CloseHandle(hMt); }

22 Synchronizacja – obiekty jądra Semafory Semafor rozszerza koncepcję muteksu na wielokrotny lecz limitowany dostęp do zasobów. –Tworząc semafor podaje się maksymalną oraz bieżącą wartość licznika dostępów do zasobów (strefy krytycznej). –Za każdym odwołaniem do semafora, wartość licznika zmniejszana jest o jeden. Gdy licznik osiąga wartość zero, semafor przestaje być sygnalizowany. –System nie dopuszcza ujemnej wartości licznika zasobów, ani wartości większej od maksymalnej. –Zwolnienie semafora powoduje zwiększenie licznika zasobów. Jeśli jego wartość jest większa od zera, staje się on sygnalizowany. Podstawowe funkcje: –CreateSemaphore – Utworzenie semafora lub otwarcie istniejącego. –OpenSemaphore – Otwarcie istniejącego semafora. –ReleaseSemaphore – Zwolnienie semafora, zwiększenie o określoną wartość licznika związanego z danym semaforem.

23 Synchronizacja – obiekty jądra Semafory Realizacja zadania wzajemnego wykluczania: –Tworzymy semafor przy pomocy funkcji CreateSemaphore. –Wchodząc do strefy krytycznej zmniejszamy licznik zasobów semafora np. przy pomocy funkcji WaitForSingleObject. –Kończąc strefę krytyczną zwiększamy licznik semafora przy pomocy funkcji ReleaseSemaphore. –Proces uzyskujący dostęp do uchwytu obiektu musi go zamknąć przy pomocy funkcji CloseHandle. –Operacje WaitForSingleObject oraz ReleaseSemaphore mają charakter atomowy. int main() { HANDLE hSemaphore; hSemaphore = CreateSemaphore (NULL, 3,3,"TestSemafor"); WaitForSingleObject (hSemaphore, INFINITE); printf("Im working..." ) ; Sleep(5000); ReleaseSemaphore (hSemaphore, 1,NULL); CloseHandle(hSemaphore); return 0 ; }

24 Synchronizacja – obiekty jądra Semafory HANDLE CreateSemaphore( PSECURITY_ATTRIBUTE pSA, // atrybuty bezpieczeństwa (u nas NULL) LONG lInitialCount, // początkowa wartość licznika LONG lMaximumCount, // maksymalna wartość licznika PCTSTR pszName// nazwa semafora ); BOOL ReleaseSemaphore( HANDLE hsem, // uchwyt obiektu semafora LONG lReleaseCount, // o ile zwiększyć licznik zasobu PLONG plPreviousCount// wskaźnik do zmiennej całkowitej, w której // funkcja zwróci poprzednią wartość licznika );// może być (i zazwyczaj jest) NULL

25 Synchronizacja – obiekty jądra Zdarzenia Zdarzenia są najprostszymi obiektami jądra wykorzystywanymi do synchronizacji. –Zazwyczaj wykorzystywane są do zasygnalizowania zakończenia jakiejś operacji. Istnieją dwa rodzaje obiektów zdarzeń: resetowane automatycznie i ręcznie. –Z chwilą zasygnalizowania zdarzenia resetowanego ręcznie wszystkie wątki oczekujące na to zdarzenie wznawiają swoje działanie. Aby stało się ono ponownie niesygnalizowane należy wywołać odpowiednią funkcję. –Gdy zostanie zasygnalizowane zdarzenie resetowane automatycznie, tylko jeden z oczekujących wątków wznawia swoje działanie, a obiekt zdarzenia staje się ponownie niesygnalizowany.

26 Synchronizacja – obiekty jądra Zdarzenia Podstawowe funkcje: –CreateEvent – Utworzenie zdarzenia lub otwarcie istniejącego. –OpenEvent – Otwarcie istniejącego zdarzenia. –SetEvent – Sygnalizacja zdarzenia. –ResetEvent – Zerowanie zdarzenia – ustawienie ponownie w stanie niesygnalizowanym. HANDLE CreateEvent( PSECURITY_ATTRIBUTES pSA, // atrybuty bezpieczeństwa (u nas NULL) BOOL fManualReset, // TRUE – resetowane ręcznie, FALSE - automatycznie BOOL fInitialState, // TRUE – zasygnalizowany, FALSE - niesygnalizowany PCTSTR pszName// nazwa zdarzenia );

27 HANDLE g_hEvent; //uchwyt do zdarzenia DWORD WINAPI SpellCheck (PVOID pvParam) { // Czekaj na wczytanie danych do pamięci WaitForSingleObject(g_hEvent, INFINITE); // OK. Dane w pamięci - przetwarzaj... return(0); } DWORD WINAPI GrammarCheck (PVOID pvParam) { // Czekaj na wczytanie danych do pamięci WaitForSingleObject(g_hEvent, INFINITE); // OK. Dane w pamięci - przetwarzaj... return(0); } int WINAPI WinMain(...) { g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); HANDLE hThread[2]; hThread[0] = _beginthreadex(NULL, 0, SpellCheck, NULL, 0, NULL); hThread[1] = _beginthreadex(NULL, 0, GrammarCheck, NULL, 0, NULL); OpenFileAndReadContentsIntoMemory(...); // Wczytanie SetEvent(g_hEvent); // OK. dane wczytane, no to wio WaitForMultipleObjects(2, hThread, TRUE, INFINITE); //poczekaj na wątki // Wątki skończyły, przetwarzaj wyniki ich pracy CloseHandle(hThread[0]); CloseHandle(hThread[1]); CloseHandle(g_hEvent);... }

28 Czytanie – pisanie Sformułowanie problemu Kolejny standardowy problem programowania współbieżnego związany jest z sytuacją, gdy w systemie mamy szereg wątków (procesów), odczytujących pewne dane oraz szereg wątków (procesów), które te dane zapisują. –Wątki (procesy) zapisujące będziemy dalej nazywać czasami pisarzami, a wątki (procesy) odczytujące – czytelnikami. Zauważmy, że wiele wątków (procesów) może odczytywać dane jednocześnie. Jednak jeśli któryś chce dane zmodyfikować, to rozsądnie jest na czas zapisu zablokować do nich dostęp dla pozostałych wątków (procesów). –Zapobiegnie to odczytaniu niespójnych informacji (na przykład danych częściowo tylko zmodyfikowanych). –Również jest to niezbędne dla wyeliminowania niespójnych modyfikacji przez kilka wątków (procesów) piszących.

29 Czytanie – pisanie Sformułowanie problemu Ogólny schemat synchronizacji działania współbieżnych wątków (procesów) możemy więc przedstawić następująco: –Gdy jeden wątek (proces) zapisuje dane, żaden inny nie może tego robić równocześnie. –Gdy jeden wątek (proces) zapisuje dane, żaden inny nie może ich w tym czasie czytać. –Gdy jeden wątek (proces) czyta dane, żaden inny nie może ich w tym czasie zapisywać. –Gdy jeden wątek (proces) czyta dane, inne mogą to robić równocześnie. Jak łatwo zauważyć problem współdzielonych blokad czytanie – pisanie pełni fundamentalną rolę, przy zarządzaniu współbieżnymi dostępami do danych. –Podstawa synchronizacji w serwerach baz danych.

30 Czytanie – pisanie Zarys rozwiązania CRITICAL_SECTION mtx; HANDLE stop; int cz = 0; void czytelnik(void*) { while(1) { EnterCriticalSection(&mtx); if (++cz == 1 ) WaitForSingleObject( stop,INFINITE); LeaveCriticalSection(&mtx); czytajdane(); EnterCriticalSection(&mtx); if (--cz == 0 ) ReleaseMutex(stop); LeaveCriticalSection(&mtx); } void pisarz(void*) { while(1) { WaitForSingleObject( stop,INFINITE); piszdane(); ReleaseMutex(stop); } void main() { stop = CreateMutex( NULL, FALSE, NULL); InitializeCriticalSection(&mtx); _beginthread(czytelnik, 0, NULL); _beginthread(pisarz, 0, NULL);... }


Pobierz ppt "Systemy rozproszone W. Bartkiewicz Wykład 9. Wprowadzenie do koordynacji programów współbieżnych."

Podobne prezentacje


Reklamy Google