15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

11
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ę.

description

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 - PowerPoint PPT Presentation

Transcript of 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

Page 1: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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.

Page 2: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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.

Page 3: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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).

Page 4: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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).

Page 5: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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.

Page 6: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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

Page 7: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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).

Page 8: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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”.

Page 9: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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.

Page 10: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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

Page 11: 15. MECHANIZMY SYNCHRONIZACJI WĄTKÓW

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.