Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
OpublikowałJustyna Majek Został zmieniony 10 lat temu
1
15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW Większość koncepcji stworzonych na potrzeby synchronizacji procesów ciężkich została zastosowana też do synchronizacji wątków, ale z pewnymi zmianami. Intencją implementatorów było, aby biblioteka funkcji obsługujących wątki (w przypadku Linuksa biblioteka pthread) była oddzielnym, samowystarczalnym narzędziem. Zalecana jest duża ostrożność w przypadku używania w jednym programie zarówno funkcji mogących blokować procesy, jak i funkcji mogących blokować wątki (skutki mogą być nieokreślone). W systemach Linux można spotkać dwie realizacje biblioteki pthread: - LinuxThreads (starsza, obecnie już nie wspierana); - NPTL (Native POSIX Thread Library) – oparta na własnościach jądra systemu od 2.6 w górę. Norma POSIX w odniesieniu do wątków w dużym stopniu wzoruje się na realizacji biblioteki threads w systemach Solaris firmy Sun.
2
Obecna realizacja NPTL jest jeszcze dość odległa od zaimplementowania w całości standardu POSIX (nie są dostępne jeszcze wszystkie przewidziane obiekty synchronizacji wątków). Realizacja funkcji synchronizujących wątki oparta jest na funkcji systemowej futex( ) (która nie jest jeszcze przenośna na wszystkie platformy sprzętowe, na których działa Linux). Wybrane źródła informacji: man pthreads (w systemach linuksowych); http://linux.die.net (opisy wszystkich funkcji przewidzianych przez POSIX). Wśród mechanizmów synchronizacji wątków zrealizowanych w systemach Linux należy wymienić: - wcielenie (join); - unieważnienie (cancel); - muteksy (mutex); - zmienne warunkowe (condition variable); - obsługę sygnałów (signal) przez wątki; - semafory (semaphore) POSIX.
3
Przy okazji omawiania zagadnień synchronizacji wątków na ogół omawiane są również tak zwane dane specyficzne wątków (thread specific data, TSD), które nie są mechanizmem synchronizacji, i które stanowią, w pewnym sensie, przeciwieństwo pojęcia pamięci dzielonej dla procesów. Wśród mechanizmów nie zrealizowanych jeszcze w Linuksach (a zrealizowanych w systemach Solaris) należy wymienić: - blokady zapisu/odczytu (read/write lock, RW lock); - blokady wirujące (spin lock); - bariery (barrier). Większość spośród wyżej wymienionych mechanizmów synchronizacji realizowanych jest przez obiekty synchronizacyjne, które, zgodnie z normą POSIX, powinny być tworzone i usuwane dynamicznie w programie, i których początkowy stan powinien być określony przez wskazanie na pewien obiekt atrybutów (podobnie, jak w przypadku samych wątków). Zgodnie z normą POSIX powinno być możliwe umieszczanie obiektów synchronizacyjnych w segmentach pamięci wspólnej (w celu synchronizacji wątków niespokrewnionych).
4
Unieważnienie Unieważnienie polega na wysłaniu jednemu wątkowi przez drugi żądania zakończenia działania (czyli przeskoku do funkcji pthread_exit( )). Żądanie takie formalnie nie jest zaliczane do sygnałów, choć ma podobny charakter (i podobną wewnętrzną realizację) – wątek otrzymuje je asynchronicznie (czyli w nieprzewidzianym przez siebie momencie). Ze względu na sposób reagowania na takie żądania każdemu wątkowi przypisany jest stan oraz typ, które można zmieniać odpowiednimi funkcjami. Są dwa stany: 1) ignorowania żądań (PTHREAD_CANCEL_DISABLE); 2) przyjmowania żądań (PTHREAD_CANCEL_ENABLE). Jeśli wątek jest w stanie przyjmowania żądań, to typ reakcji może być: 1) natychmiastowy (PTHREAD_CANCEL_ASYNCHRONOUS); 2) opóźniony (PTHREAD_CANCEL_DEFERRED).
5
Jeśli typ reakcji został ustalony jako opóźniony, to wątek wywołuje funkcję pthread_exit( ) dopiero po dotarciu do najbliższego (chronologicznie) punktu unieważnienia (cancellation point) w swoim programie. Standard POSIX podaje wykaz funkcji (można go znaleźć na przykład w man pthreads), których miejsca wywołań w programie powinny być uważane za punkty unieważnienia. Zazwyczaj są to funkcje mogące blokować wątki, na przykład pthread_join( ), pthread_cond_wait( ), pthread_cond_timedwait( ), sem_wait( ), sigwait( ), ale może też to być nieblokująca funkcja pthread_testcancel( ), jak również funkcje mogące blokować procesy, takie jak read( ) czy write( ). W powyższym zakresie standard POSIX prawdopodobnie jeszcze nigdzie nie został zaimplementowany w całości. Poza wspomnianym wyżej wykazem, POSIX zaleca, aby w przyszłych implementacjach wszystkie funkcje potencjalnie blokujące były traktowane jako punkty unieważnienia.
6
int pthread_cancel (pthread_t id); Zwraca: 0 w przypadku sukcesu niezerowy kod w przypadku błędu id – identyfikator wątku Działanie: wysyła żądanie zakończenia do wątku o identyfikatorze id. int pthread_setcancelstate (int stan, int *poprzedni_stan); Zwraca: 0 w przypadku sukcesu niezerowy kod w przypadku błędu stan – nowy stan, jaki ma być przyjęty przez wywołujący wątek poprzedni_stan – jeśli nie jest NULL, to pod tym adresem będzie pamiętany dotychczasowy stan
7
Działanie: ustala nowy stan wątku, który wywołał tę funkcję (i ewentualnie zapamiętuje dotychczasowy stan). int pthread_setcanceltype (int typ, int *poprzedni_typ); Zwraca: 0 w przypadku sukcesu niezerowy kod w przypadku błędu typ – nowy typ reakcji na żądanie zakończenia działania poprzedni_typ – jeśli nie jest NULL, to pod tym adresem będzie pamiętany dotychczasowy typ Działanie: ustala nowy typ reakcji wątku, który wywołał tę funkcję (i ewentualnie zapamiętuje dotychczasowy typ). void pthread_testcancel (void); Działanie: ustanawia punkt unieważnienia (i nic więcej).
8
Sygnały wątkowe Z powodu wprowadzenia wielowątkowości działanie sygnałów w systemach uniksowych znacznie skomplikowało się. Jednym ze skutków jest to, że obecnie w programach wielowątkowych nie jest zalecane używanie tradycyjnej funkcji signal ( ) (jej działanie może dać skutki nieokreślone) – zamiast niej zalecana jest (zgodna z normą POSIX) funkcja sigaction( ). W celu implementacji odwzorowania wykonywanych wątków na procesy lekkie jądra systemu wprowadzono kilka dodatkowych sygnałów (o numerach powyżej 30) – ich bezpośrednie użycie w programach jest niezalecane. W przypadku procesów zawierających wiele wątków można rozpatrywać następujące sytuacje: - wątek sam spowodował konieczność wysłania sygnału przez jądro systemu (na przykład wskutek próby dzielenia przez 0) – w takim przypadku ten właśnie wątek będzie odbiorcą sygnału; - wątek wysłał sygnał do wątku spokrewnionego wywołując funkcję pthread_kill ( ) – w takim przypadku sygnał otrzyma jego adresat; - do procesu dotarł sygnał asynchroniczny z zewnątrz.
9
W tej ostatniej sytuacji może mieć miejsce: a) zatrzymanie całego procesu (szczególnie w przypadku sygnału nieprzechwytywalnego SIGKILL); b) obsługa sygnału przez jeden z wątków procesu (jeśli maski sygnałów umożliwiają to kilku wątkom, wybór jednego z nich będzie niedeterministyczny). int pthread_sigmask (int sposób, const sigset_t *nowa_maska, sigset_t *poprzednia_maska); Zwraca: 0 w przypadku sukcesu; niezerowy kod w przypadku błędu sposób – SIG_SETMASK, SIG_BLOCK lub SIG_UNBLOCK nowa_maska – wskaźnik na wykaz sygnałów poprzednia_maska - jeśli nie jest NULL, to pod tym adresem będzie pamiętana dotychczasowa maska Działanie: jeśli sposób jest równy SIG_SETMASK, to ustawia maskę sygnałów wywołującego wątku dokładnie na podaną, jeśli SIG_BLOCK, to nowe sygnały są dokładane do dotychczasowych, a jeśli SIG_UNBLOCK, to usuwane spośród dotychczasowych.
10
int pthread_kill (pthread_t id, int sygnał); Zwraca: 0 w przypadku sukcesu niozerowy kod w przypadku błędu id – identyfikator wątku, do którego wysyłany jest sygnał sygnał – identyfikator rodzaju sygnału Działanie: wysyła dany sygnał do danego wątku. int sigwait (const sigset_t *zbiór, int *sygnał); Zwraca: zawsze 0 zbiór – wskaźnik na zbiór sygnałów, na jakie wątek ma czekać sygnał – jeśli nie jest NULL, to pod tym adresem będzie zapamiętany sygnał, który przyjdzie jako pierwszy z podanego zbioru
11
Działanie: wywołujący wątek zostaje zawieszony aż do nadejścia dowolnego z sygnałów z podanego zbioru – wtedy identyfikator tego sygnału zostaje zapamiętany pod adresem podanym przez drugi argument funkcji. Sygnał musi być uwzględniony w masce sygnałów danego wątku (nie może być zablokowany). Jeśli z sygnałem związana jest jakaś funkcja jego obsługi (zarejestrowana dla całego procesu), nie jest ona wykonywana. Uwaga: Funkcje obsługi sygnałów przychodzących z zewnątrz procesu są zarejestrowane dla całego procesu (wszystkie wątki procesu je wspóldzielą), ale są wykonywane (w razie potrzeby) przez pojedynczy wątek. Wątki posiadają swoje indywidualne maski sygnałów. Wątek, który ma wykonać funkcję obsługi przybyłego sygnału, za każdym razem jest niedeterministycznie wybierany spośród tych wątków, które w danej chwili mają ten sygnał uwzględniony w swojej masce.
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.