1 / 30

Systemy rozproszone

Uniwersytet Łódzki Katedra Informatyki. W. Bartkiewicz. Systemy rozproszone. Wykład 9. Wprowadzenie do koordynacji programów współbieżnych. Katedra Informatyki. Współbieżność.

hafwen
Download Presentation

Systemy rozproszone

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. Katedra Informatyki 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. Katedra Informatyki SynchronizacjaWzajemne 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. Katedra Informatyki 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. Katedra Informatyki 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. Katedra Informatyki 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): • Wątek fun1 sprawdza, że enter1 ma wartość true. • Wątek fun1 zostaje wywłaszczony (lub pechowa kolejność operacji równoległych). • 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. • Wątek fun2 zostaje wywłaszczony. • 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. Katedra Informatyki 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: • Wątek fun1 ustawia enter2 na false. • Wątek fun1 zostaje wywłaszczony (lub pechowa kolejność operacji równoległych). • Wątek fun2 ustawia enter1 na false. • Wątek fun2 sprawdza, że enter2 ma wartość false i jest zablokowany przez pętlę while. • Wątek fun2 zostaje wywłaszczony. • Wątek fun1 sprawdza enter2 i też jest zablokowany przez pętlę while. • Obydwa wątki są zablokowane i czekają na siebie. • Oooops!! Zakleszczenie

  8. Katedra Informatyki 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. Katedra Informatyki 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. Katedra Informatyki SynchronizacjaMS 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. Katedra Informatyki Synchronizacja w trybie użytkownikaSekcje krytyczne 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); } • 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.

  12. Katedra Informatyki Synchronizacja w trybie użytkownikaSekcje krytyczne • 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.

  13. Katedra Informatyki Synchronizacja w trybie użytkownikaSekcje krytyczne • 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.

  14. Katedra Informatyki Synchronizacja w trybie użytkownikaSekcje krytyczne • 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ń.

  15. Katedra Informatyki 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. Katedra Informatyki 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. Katedra Informatyki Synchronizacja – obiekty jądraMuteksy • 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. Katedra Informatyki Synchronizacja – obiekty jądraMuteksy 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); } • 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.

  19. Katedra Informatyki Synchronizacja – obiekty jądraMuteksy HANDLE CreateMutex( PSECURITY_ATTRIBUTES pSA, // atrybuty bezpieczeństwa (u nas NULL) BOOL fInitialOwner, // czy muteks od razu zajęty PCTSTR pszName // nazwa muteksu ); • 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.

  20. Katedra Informatyki Synchronizacja – obiekty jądraMuteksy HANDLE CreateMutex( PSECURITY_ATTRIBUTES pSA, // atrybuty bezpieczeństwa (u nas NULL) BOOL fInitialOwner, // czy muteks od razu zajęty PCTSTR pszName // nazwa muteksu ); • 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.

  21. Katedra Informatyki Synchronizacja – obiekty jądraMuteksy //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. Katedra Informatyki Synchronizacja – obiekty jądraSemafory • 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. Katedra Informatyki Synchronizacja – obiekty jądraSemafory int main(){ HANDLE hSemaphore; hSemaphore=CreateSemaphore (NULL,3 ,3 ,"TestSemafor"); WaitForSingleObject (hSemaphore, INFINITE); printf("I’m working..." ) ; Sleep(5000); ReleaseSemaphore (hSemaphore , 1 ,NULL); CloseHandle(hSemaphore); return 0 ; } • 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.

  24. Katedra Informatyki Synchronizacja – obiekty jądraSemafory 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. Katedra Informatyki Synchronizacja – obiekty jądraZdarzenia • 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. Katedra Informatyki Synchronizacja – obiekty jądraZdarzenia 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 ); • 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.

  27. Katedra Informatyki 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. Katedra Informatyki Czytanie – pisanieSformuł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. Katedra Informatyki Czytanie – pisanieSformuł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. Katedra Informatyki 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); ... }

More Related