POSIX Threads Wojciech Mula marzec 2010 z późniejszymi poprawkami (plik utworzony 21 marca 2011) Najbardziej aktualna wersja online jest utrzymywana w serwisie Wikibooks. Wersja PDF jest dostępna adresem 0x80.pl. Sklad wykonano programem L A T E X2 ε
105
Embed
POSIX Threads - Wojciech Muła — website0x80.pl/articles/pthreads.pdf · Niniejszy podręcznik ma na celu zaznajomienie Czytelnika z biblioteką programistyczną POSIX Threads (w
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
POSIX ThreadsWojciech Muła
marzec 2010 z późniejszymi poprawkami(plik utworzony 21 marca 2011)
Najbardziej aktualna wersja online jest utrzymywana w serwisie Wikibooks.Wersja PDF jest dostępna adresem 0x80.pl.
Niniejszy podręcznik ma na celu zaznajomienie Czytelnika z biblioteką programistyczną POSIXThreads (w skrócie pthreads). Biblioteka udostępnia jednolite API dla język C do tworzenia i zarzą-dzania wątkami , jest dostępna w systemach m.in. Linux , FreeBSD , Windows .
Specyfikacja POSIX jest dostępna nieodpłatnie na stronie organizacji Open Group (http://www.opengroup.org/). Dla wygody Czytelnika linkujemy w tym podręczniku do konkretnych podstron opi-sujących funkcje.
Opis części implementacji dla systemu Linux znajduje się na stronie The Linux man-pages project(http://www.kernel.org/doc/man-pages/).
Niektóre strony podręczników zawierają przykładowe programy.
1.2.1 Pliki nagłówkowe
Podstawowy plik nagłówkowy biblioteki POSIX Threads nazywa się pthread.h i jego włączeniedo programu jest konieczne. Dokładny wykaz funkcji znajdujący się w tym pliku, wraz z adnotacjamiopcji, znajduje się na stronie http://www.opengroup.org/onlinepubs/009695399/basedefs/pthread.h.html.
Oprócz niego:
• sys/types.h — typy danych (domyślnie włączane przez pthreads.h)
• limits.h — stałe określające ograniczenia biblioteki
1.2.2 Konwencje nazewnicze
Nazwy funkcji i typów zaczynają się zawsze od pthread , stałe są pisane wielkimi literami i podob-nie rozpoczynają od PTHREAD . Nazewnictwo poza tym jest bardzo konsekwentne, podobnie kolejnośćargumentów itd.
1.2.3 Typy danych
Typy definiowane przez bibliotekę powinny być traktowane jako abstrakcyjne — jedynym sposobemich inicjalizacji, zmiany wartości, itd. jest użycie dedykowanych funkcji.
UWAGA
Nie można po zainicjowaniu obiektu utworzyć jego kopii np. wykonując funkcjęmemcpy, ponieważ biblioteka może równolegle utrzymywać jakieś dodatkoweinformacje dotyczące danego obiektu.
Nazwy funkcji tworzących obiekty są zgodne ze schematem pthread XXX init, natomiast niszcząceobiekt pthread XXX destroy.
1.2.4 Atrybuty
Wszelkie dodatkowe parametry obiektów tworzonych w bibliotece są opisywane atrybutami, tj. inny-mi obiektami, które przechowują określone parametry. Obiekty atrybutów mogą być używane wielokrotniedo tworzenia różnych obiektów.
1.2.5 Zgłaszanie błędów
Większość funkcji z pthreads zwraca wartości typu int, która określa status operacji. Jeśli jest równyzero, funkcja wykonała się poprawnie, w przeciwnym razie zwracana jest standardowa wartość błędu (wrodzaju EINVAL, ENOMEM, EBUSY).
Można więc używać funkcji strerror, ewentualnie przypisywać wynik do zmiennej errno i korzystaćz funkcji perror. Standard POSIX określa, że errno w środowisku wielowątkowym jest lokalne względemwątku.
Na przykład:
\begin{enumerate}
\item include <string.h>\item include <errno.h>\end{enumerate}int main() {int status;
status = pthread_XXX(...);if (status)printf("Błąd przy wywoływaniu pthread_XXX: %s", strerror(status));
Wszystkie dane użytkownika są przekazywane przez wskaźniki typu void*. O interpretacji wskaźnikówi danych na które wskazują decyduje wyłącznie programista, biblioteka pthreads nie określa ani nieogranicza tego w żaden sposób.
1.2.7 Kompilacja
Ponieważ pthreads jest zewnętrzną biblioteką należy linkerowi podać ścieżkę do pliku bibliotecznego.Np. przy kompilacji gcc należy dodać opcję -lpthread.
1.3 Opcje standardu
Implementacja biblioteki pthreads nie musi dostarczać wszystkich funkcji opisanych w standardzie,dopuszcza on szereg opcjonalnych rozszerzeń. W tym podręczniku posługujemy się skrótami używanymina stronach Open Group:
• możliwość wyboru zegara odmierzającego czas przy oczekiwaniu na zmienną warunkową; domyślnieużywany jest zegar systemowy (CS — Clock Selection)
• czas procesora zużyty przez wątek (TCT — Thread CPU-Time Clocks)
• możliwość ograniczonego czasowo oczekiwania na uzyskanie blokad (mutexy) oraz blokad do odczy-tu/zapisu (TMO — Timeouts)
1.4 C++
Użycie biblioteki pthreads w programach pisanych w języku C++ jest oczywiście możliwe. Należyjednie w procedurach wykonywanych w wątkach (patrz Tworzenie wątku) obsługiwać wszystkie wyjątki:
Uwaga! Przerywanie wątków w implementacji NPTL jest realizowane poprzez zgłoszenie wyjąt-ku — jeśli w wątku zostanie użyte catch (...), wówczas program zakończy się komunikatem FATAL:exception not rethrown. Urlich Drepper [http://udrepper.livejournal.com/ wyjaśnia na swoim blogu],jak objeść ten problem.
1.5 O podręczniku
1.5.1 Przykłady
Przykładowe programy mają jedynie na celu zilustrowanie pewnych cech biblioteki. Jednocześniesą to w pełni funkcjonalne programy — każdy może je skopiować do swojego komputera, skompilowaći uruchomić. Mają być ułatwieniem dla własnych eksperymentów lub testów.
W programie korzystającym z biblioteki pthreads, tuż po uruchomieniu działa dokładnie jeden wątek,wykonujący funkcję main.
Aby utworzyć nowe wątki programista podaje funkcję (dokładniej: adres funkcji), która ma zostaćw nim wykonana — pthread create tworzy i od razu uruchamia wątek, oraz zwraca jego identyfikator.Oczywiście jedna funkcja może być wykorzystywana do utworzenia wielu wątków. Funkcja użytkownikaprzyjmuje i zwraca wartość typu void*, interpretacja tych danych jest zależna od programu, pthreadsnie narzuca więc tutaj żadnych ograniczeń. Wątki mogą być tworzone z poziomu dowolnego innego wątku.
Wątek identyfikuje wartość pthread t, która jest argumentem dla większości funkcji bibliotecznych.Liczba wątków, jakie można utworzyć jest ograniczona przez stałą PTHREAD THREADS MAX (z pliku
limits.h) oraz rzecz jasna przez zasoby systemowe.Infobox—W Cygwinie PTHREAD THREADS MAX nie jest zdefiniowane, ponieważ system MS Windows
pozwala utworzyć dowolną liczbę wątków
2.1.1 Typy
• pthread t
– identyfikator wątku
• pthread attr t
– atrybutów wątku
2.1.2 Funkcje
• int pthread create(pthread t *id, const pthread attr t *attr, void* (fun*)(void*),void* arg)
– id — identyfikator wątku;
– attr — wskaźnik na atrybuty wątku, określające szczegóły dotyczące wątku; można podaćNULL, wówczas zostaną użyte domyślne wartości;
– fun — funkcja wykonywana w wątku; przyjmuje argument typu void* i zwraca wartość tegosamego typu;
– arg — przekazywany do funkcji.
2.1.3 Przykład
Poniższy program typu hello world tworzy (w wątku głównym) kilka innych wątków, wykonującychfunkcję watek i czeka na ich zakończenie. Używane są domyślne atrybuty wątku.
⇒ Przejdź do przykładowego programu nr 1.
2.2 Identyfikator wątku
Wewnętrzna struktura typu pthrad t (podobnie jak innych używanych w pthreads) jest określonaprzez implementację. Jedyną operacją, jaką można wykonać na identyfikatorach jest ich porównanie zewzględu na równość funkcją pthread equal.
Funkcja pthread self zwraca identyfikator wywołującego wątku, co czasem jest przydatne.
– stwierdzenie, czy identyfikatory wątków są równe
• pthread t pthread self()
– zwraca identyfikator wywołującego wątku
2.3 Kończenie wątku
Wątek jest kończony w chwili, gdy funkcja użytkownika przekazana w pthread create zwraca stero-wanie do wywołującego ją kodu, a więc w miejscu wystąpienia instrukcji return.
Ponadto istnieje funkcja pthread exit, która powoduje zakończenie wątku — może zostać użytaw funkcjach wywoływanych z funkcji wątku.
2.3.1 Przykład
W przykładowym programie zakończenie wątku powoduje wywołanie pthread exit na 6. poziomiezagnieżdżenia funkcji koniec watku.
Oczekiwanie na zakończenie wątku jest dodatkowym sposobem synchronizacji między wątkami i doty-czy wyłącznie wątków typu joinable (zobacz Rodzaje wątków poniżej). Wątek wywołujący pthread joinzostaje wstrzymany do chwili, gdy wskazany wątek zakończy działanie — wówczas wątek oczekujący jestkontynuowany, a pthread join przekazuje wartość wynikową zwróconą przez wątek. Jeśli wątek zostałprzerwany wartością zwracaną jest stała PTHREAD CANCELED.
UWAGA
Jeśli wątek ”zapętli się”, oczekujący na jego zakończenie inny wątek nie jestjuż w stanie nic zrobić.
Oczekiwanie na zakończenie wątku można uzyskać zmiennymi warunkowymi (dodatkowo bez ograni-czań na rodzaj wątku), wymaga to jednak dodatkowej pracy ze strony programisty.
2.4.1 Funkcje
• int pthread join(pthrea t id, void **retval)
– id — identyfikator wątku, którego zakończenie jest oczekiwane;
– retval — wskaźnik na wartość wynikową wątku; może być NULL, wówczas wynik jest ignoro-wany
Kiedy kończy się proces, wszystkie wątki utworzone w jego obrębie są również (gwałtownie) kończone.Dlatego koniecznie trzeba użyć albo wbudowanego mechanizmu synchronizacji, albo jakiegoś własnegorozwiązania.
2.6 Rodzaje wątków
W pthreads wątki są dwojakiego rodzaju:
• joinable (domyślny rodzaj)
• detached
Rodzaj jest ustalany w atrybutach wątku. Można jednak po utworzeniu wątku zmienić typ z joinable nadetached funkcją pthread detach; odwrotne działanie nie jest możliwe.
Wątek typu joinable to taki, z którego można odczytać wartość wynikową zwróconą przez funkcję. Gdywątek tego typu kończy się, jego zasoby nie są zwalniane do chwili wywołania funkcji pthread join(patrz Oczekiwanie na zakończenie wątku).
Warto więc zwrócić uwagę, że utworzenie wątku typu joinable i nie wywołanie wspomnianej funk-cji skutkować będzie wyciekiem pamięci (następstwem którego tworzenie nowych wątków może stać sięniemożliwe).
Wątek typu detached z chwilą zakończenia działania od razu zwalnia wszystkie zasoby; funkcjapthread join nie akceptuje identyfikatorów do wątków tego typu.
2.6.1 Funkcje
• int pthread detach(pthread t id)
– zmiana rodzaju wątku
2.7 Przekazywanie argumentów i zwracanie wyników
2.7.1 Przekazywanie argumentów
Funkcja wykonywana w wątku przejmuje argument typu void* podawany w funkcjipthread create — wskaźnik ten może więc wskazywać dowolną strukturę, może być także pusty.
UWAGA
Ponieważ przekazywane są wskaźniki, dla każdego trzeba wątku przypisaćosobny obiekt argumentów
Można również rzutować bezpośrednio typy, których rozmiar nie przekracza rozmiaru wskaźnik, tzn.gdy sizeof(typ) <= sizeof(void*); mogą to być typy int, short, char, być może też inne — zależnieod platformy sprzętowej i kompilatora.
2.7.2 Zwracanie wyniku
Funkcja użytkownika zwraca wskaźnik na void*. Jeśli potrzeba zwrócić jakieś dane, należy je zaalo-kować na stercie funkcją malloc lub podobną.
UWAGA
Zwracanie wskaźników do obiektów lokalnych (utworzonych na stosie) jest błę-dem — nie tylko w programach wielowątkowych!
Można również rzutować na void*, tak samo jak w przypadku przekazywania argumentów.
2.7.3 Przykład
⇒ Przejdź do przykładowego programu nr 2.
Wyjście:
$ ./przykladWitaj Wikibooks w dniu 2010-03-14Wątek 2 wywołany z argumentem liczbowym 27wątek 2 zwrócił napis: ’xxxxxxxxxxxxxxxxxxxxxxxxxxx’
13
2.8. ATRYBUTY WĄTKÓW
2.8 Atrybuty wątków
Atrybuty wątku pozwalają ustawić szereg parametrów wątków podczas ich tworzenia. Pojedynczyobiekt atrybutów może być wykorzystany wielokrotnie do tworzenia różnych wątków.
Wszystkie atrybuty wątku opisuje typ pthread attr t. Nazwy funkcji operujących na tym ty-pie zaczynają się od pthread attr. Funkcje ustawiające poszczególne atrybuty zaczynają się odpthread attr set i istnieją dla nich odpowiedniki odczytujące pthread attr get.
2.9 Inicjalizacja
Przed użyciem atrybutu zmienna musi zostać zainicjowana funkcją pthread attr init, zwolnieniezasobów z nim związanych realizuje funkcja pthread attr destroy.
Rodzaje wątków zostały dokładniej opisane w innej sekcji. Funkcje pthread attr setdetachstateustawia, zaś pthread attr getdetachstate odczytuje rodzaj wątku, jaki ma zostać ustalony przy jegotworzeniu. Rodzaj jest identyfikowany jedną z wartości:
• PTHREAD CREATE JOINABLE — utworzenie wątku typu joinable (domyślnie);
• PTHREAD CREATE DETACHED — utworzenie wątku typu detached.
2.10.1 Funkcje
• int pthread attr setdetachstate(pthread attr t *attr, int detachstate)
– ustawienie rodzaju
• int pthread attr getdetachstate(const pthread attr t *attr, int *detachstate)
Domyślny rozmiar stosu wątku zależy od implementacji i może być rzędu kilku-kilkudziesięciu kilo-bajtów lub kilku megabajtów. Należy liczyć się z tym, że rozmiar będzie wewnętrznie zaokrąglony dorozmiaru strony pamięci (PAGE SIZE — 4kB na procesorach x86). Zmiana rozmiaru jest możliwa jeżelibiblioteka pthreads implementuje opcję TSS.
Samodzielne ustalenie rozmiaru stosu może być konieczne, gdy funkcja wątku tworzy duże obiekty nastosie lub przeciwnie — gdy wiadomo, że wątki nie potrzebują zbyt wiele pamięci, a jednocześnie będziepotrzebna dużą ich liczba. Rozmiar stosu nie może być mniejszy od stałej PTHREAD STACK MIN (z plikulimits.h) ani przekraczać możliwości systemu.
Jeśli biblioteka implementuje opcję TSA można również ustalić adres stosu.
UWAGA
Zmiana adresu stosu może być nieprzenośne ze względu na to, że wskaźnikstosu może albo zmniejszać albo zwiększać adres przy odkładaniu elementówna stosie
2.11.1 Funkcja
• int pthread attr setstacksize(pthread attr t *attr, size t stacksize)
– ustalenie nowego rozmiaru stacksize
• int pthread attr getstacksize(const pthread attr t *attr, size t *stacksize)
– odczyt rozmiaru
• int pthread attr setstackaddr(pthread attr t *attr, void *stackaddr)
– ustalenie nowego rozmiaru stosu stackaddr
• int pthread attr getstackaddr(const pthread attr t *attr, void **stackaddr)
– odczyt adresu
• int pthread attr setstack(pthread attr t *attr, void *stackaddr, size t stacksize)
– jednoczesne ustalenie adresu i rozmiaru stosu
• int pthread attr getstack(const pthread attr t *attr, void **stackaddr, size t *stacksize)
– odczyt adresu i rozmiaru
2.11.2 Przykład
W programie uruchamiany jest wątek, który na stosie alokuje względnie dużą tablicę (ok. 200kB),następnie tablica jest czyszczona. Program z linii poleceń odczytuje żądany rozmiar stosu — ustawiającjego wartość na zbyt małą z pewnością doprowadzimy do błędu SIGSEGV.
⇒ Przejdź do przykładowego programu nr 12.
2.12 Obszar zabezpieczający stosu
Opcja XSI . Jeśli rozmiar obszaru zabezpieczającego (guard) jest większy do zera, za stosem wątkurezerwowana jest pamięć (o rozmiarze zaokrąglonym w górę do rozmiaru strony, tj. PAGE SIZE), która niemoże być zapisywana ani odczytywana. Ułatwia to detekcję części powszechnych błędów polegających nawyjściu poza stos, czyli np. jego przepełnienie, są bowiem sygnalizowane przez sygnał SIGSEGV.
Domyślnie obszar ten jest włączony i ma minimalną wielkość.
• int pthread attr setguardsize(pthread attr t *attr, size t guardsize)
– ustawienie rozmiaru obszaru zabiezpieczającego
• int pthread attr getguardsize(const pthread attr t *attr, size t *guardsize)
– jego odczyt
2.13 Szeregowanie wątków
Wątki, podobnie jak procesy, mogą działać z różnymi priorytetami i być szeregowane przez różnealgorytmy. Jest to opcja standardu TPS.
2.13.1 Dziedzicznie ustawień
Wątek tworzony funkcją pthread create może albo dziedziczyć ustawienia szeregowania z wywołu-jącego wątku, albo uwzględniać wartości z atrybutów. Ten parametr opisują dwie wartości:
Jeśli biblioteka implementuje opcję TPS (algorym SCHED SPORADIC), struktura zawiera więcej pól.Wartość priorytetu jest ograniczona wartościami zwracanymi przez funkcje
sched get priority min/max, które zależą od wybranego algorytmu szeregowania.
Funkcje
• int pthread setschedparam(pthread t thread, int policy, const struct sched param*param)
– ustawienie algorytmu i jego parametrów
• int pthread getschedparam(pthread t thread, int *policy, struct sched param*param)
– odczyt
Info—odczytane parametry nie uwzględniają chwilowych zmian priorytetów, np. spowodowanych ochronąpriorytetów w sekcjach krytycznych.
2.13.4 Przykład
Program pozwala wybrać algorytm szeregowania, po czym tworzy kilka wątków z co raz większymipriorytetami (z dopuszczalnego zakresu).
⇒ Przejdź do przykładowego programu nr 19.
Przykładowe wyjście w systemie Linux dla dostępnych algorytmów szeregowania.
$ ./przyklad 0SCHED_OTHER: priorytety w zakresie 0 ... 0utworzono wątek #0 o priorytecie 0utworzono wątek #1 o priorytecie 0utworzono wątek #2 o priorytecie 0utworzono wątek #3 o priorytecie 0wątek #0 (priorytet 0): licznik = 30630wątek #1 (priorytet 0): licznik = 30631wątek #2 (priorytet 0): licznik = 30633wątek #3 (priorytet 0): licznik = 30620
$ ./przyklad 1SCHED_RR: priorytety w zakresie 1 ... 99utworzono wątek #0 o priorytecie 1
utworzono wątek #1 o priorytecie 33utworzono wątek #2 o priorytecie 66utworzono wątek #3 o priorytecie 99wątek #0 (priorytet 1): licznik = 146812wątek #1 (priorytet 33): licznik = 150084wątek #2 (priorytet 66): licznik = 151116wątek #3 (priorytet 99): licznik = 150744
$ ./przyklad 2SCHED_FIFO: priorytety w zakresie 1 ... 99utworzono wątek #0 o priorytecie 1utworzono wątek #1 o priorytecie 33utworzono wątek #2 o priorytecie 66utworzono wątek #3 o priorytecie 99wątek #0 (priorytet 1): licznik = 146659wątek #1 (priorytet 33): licznik = 149249wątek #2 (priorytet 66): licznik = 150764wątek #3 (priorytet 99): licznik = 150895
2.14 Zakres konkurowania wątków
Pthreads pozwala opcjonalnie określić, czy szeregowanie wątków będzie wykonywane w obrębie ca-łego systemu (tzn. ze wszystkimi innymi wątkami i procesami), czy tylko w obrębie wątków z jednegoprocesu. Jest to opcja standardu TPS.
Stałe określające zakres konkurowania:
• PTHREAD SCOPE SYSTEM — system,
• PTHREAD SCOPE PROCESS — proces.
Funkcje
• int pthread attr setscope(pthread attr t *attr, int contentionscope)
– ustawienie zakresu
• int pthread attr getscope(const pthread attr t *attr, int *contentionscope)
– odczyt
2.15 Przykład
Poniższy program wyświetla wszystkie informacje nt. atrybutów wątku.
⇒ Przejdź do przykładowego programu nr 4.
Przykładowe wyjście dla domyślnych ustawień atrybutów (w systemie Linux):
Z każdym wątkiem związany jest stos funkcji finalizujących (cleanup stack) — jest to osobny stosna którym zapisywane są funkcje użytkownika i argumenty dla nich (przez pthread cleanup push), którenastępnie mogą być wywołane wprost przy ściąganiu ze stosu (pthread cleanup pop).
Ponadto stos funkcji jest automatycznie czyszczony — tzn. ściągane i wykonywane są kolejne funk-cje przy asynchronicznym przerwaniu wątku oraz gdy wątek jest kończony wywołaniem pthread exit.Jeśli wątek kończy się wykonaniem instrukcji return, wówczas to programista jest odpowiedzialny za wy-czyszczenie stosu poprzez wywołanie odpowiednią liczbę razy funkcji pthread cleanup pop. Co prawdastandard nakłada na implementację konieczność zagwarantowania, że te dwie funkcje występują w blo-ku kodu (makra preprocesora), jednak wyjście z bloku instrukcją return, break, continue lub gotojest niezdefiniowane. (Np. w Cygwinie otrzymałem błąd SIGSEGV, w Linuxie błąd został po cichuzignorowany).
Funkcja użytkownika zwraca i przyjmuje argument typu void*.Zastosowaniem opisanego mechanizmu jest zwalniania zasobów przydzielonych wątkowi — np. zwol-
nienie blokad, dealokacja pamięci, zamknięcie plików, gniazd. Jest on nieco podobny do znanego z językaC++ automatycznego wołania destruktorów przy opuszczaniu zakresu, w którym zostały utworzone.
3.1.1 Funkcje
• int pthread cleanup push(void (fun*)(void*), void *arg)
– odłożenie na stos adresu funkcji fun, która przyjmuje argument arg
• int pthread cleanup pop(int execute)
– zdjęcie ze stosu funkcji i jeśli execute jest różne od zera, wykonanie jej
3.1.2 Przykład
W przykładowym programie dwa wątki alokują pamięć. Jeden wątek wprost wywołuje funkcjępthread cleanup pop, w drugim funkcja finalizująca jest wywoływana automatycznie po wykonaniupthread exit.
⇒ Przejdź do przykładowego programu nr 5.
Wyjście:
$ ./przykladwątek #0 zaalokował 100 bajtów pod adresem 0x9058098wątek #1 zaalokował 100 bajtów pod adresem 0x9058190wątek #0 zaalokował 200 bajtów pod adresem 0x90581f8wątek #1 zaalokował 200 bajtów pod adresem 0x90582c8zwalnianie pamięci spod adresu 0x90581f8zwalnianie pamięci spod adresu 0x9058098wątek #0 zakończył sięzwalnianie pamięci spod adresu 0x90582c8zwalnianie pamięci spod adresu 0x9058190
3.2 Lokalne dane wątku
W pthreads istnieje możliwość przyporządkowania kluczom, które są jednakowe dla wszystkichwątków, wskaźnika do danych specyficznych dla danego wątku. W istocie jest to powiązanie pary (klucz,wątek) z danymi, przy czym odwołanie do danych wymaga podania jedynie klucza — wywołujący wątekjest domyślnym drugim elementem pary. Ułatwia to m.in. przekazywanie danych do funkcji wywoływanychz poziomu wątków.
Z kluczem można związać funkcję (destruktor), która jest wywoływana przy zakończeniu wątku je-śli dane wątku są różne od NULL. Gdy istnieje więcej kluczy, kolejność wywoływania destruktorów jestnieokreślona.
Jeśli klucz został utworzony (pthread key create) to dane dla nowotworzonego wątku są automa-tycznie inicjowane na wartość NULL. Błędem jest próba sięgnięcia lub zapisania danych dla nieistniejącegoklucza.
Liczba dostępnych kluczy jest ograniczona stałą PTHREAD KEY MAX.
3.2.1 Typ
• pthread key t
3.2.2 Funkcje
• int pthread key create(pthread key t *key, void (destructor*)(void*))
– utworzenie nowego klucza, przypisanie destruktora
• int pthread key delete(pthread key t key)
– usunięcie klucza
• int pthread setspecific(pthread key t key, const void *data)
– przypisanie do klucza danych wątku
• void* pthread getspecific(pthread key t key)
– pobranie danych związanych z kluczem.
3.2.3 Przykład
W programie tworzony jest jeden klucz, z którym wątki kojarzą napis — przedrostek, którym funkcjawyswietl poprzedza wyświetlane komunikaty.
⇒ Przejdź do przykładowego programu nr 10.
Przykładowe wyjście:
adres napisu: 0x9bd6008 (’***’)adres napisu: 0x9bd60a8 (’!!!’)!!!: Witaj w równoległym świecie!adres napisu: 0x9bd6148 (’###’)###: Witaj w równoległym świecie!***: Witaj w równoległym świecie!!!!: Wątek wykonuje pracę***: Wątek wykonuje pracę###: Wątek wykonuje pracę!!!: Wątek zakończonywywołano destruktor, adres pamięci do zwolnienia: 0x9bd60a8 (’!!!’)***: Wątek zakończonywywołano destruktor, adres pamięci do zwolnienia: 0x9bd6008 (’***’)###: Wątek zakończonywywołano destruktor, adres pamięci do zwolnienia: 0x9bd6148 (’###’)
Czasem istnieje potrzeba jednokrotnego wykonania jakiejś funkcji, np. w celu inicjalizacji jakiś global-nych ustawień, biblioteki, otwarcia plików, gniazd itp. Pthreads udostępnia funkcję pthread once, któraniezależnie od liczby wywołań, uruchamia dokładnie raz funkcję użytkownika.
Jednokrotne uruchomienie gwarantuje obiekt typu pthread once t; zmienną tego typu należy sta-tycznie zainicjować wartością PTHREAD ONCE INIT.
3.3.1 Typy
• pthread once t
3.3.2 Funkcje
• int pthread once(pthread once t *once, void (fun*)(void))
3.3.3 Przykład
⇒ Przejdź do przykładowego programu nr 6.
Wynik:
$ ./przykladRozpoczynanie programuUruchomiono wątek nr 0Uruchomiono wątek nr 2Uruchomiono wątek nr 1Uruchomiono wątek nr 3Uruchomiono wątek nr 4Uruchomiono wątek nr 5Uruchomiono wątek nr 6Uruchomiono wątek nr 7Uruchomiono wątek nr 8Uruchomiono wątek nr 9
3.4 UNIX-owe sygnały
Gdy sygnał zostanie dostarczony do procesu nie jest określone w kontekście którego wątku wykonasię procedura obsługi sygnału.
3.4.1 Blokowanie sygnałów
Pthreads umożliwia zablokowanie określonych sygnałów na poziomie wątków, służy temu funkcjapthread sigmask (analogiczna do sigprocmask), która modyfikuje zbiór zablokowanych sygnałów wy-wołującego ją wątku. Nie można zablokować SIGFPE, SIGILL, SIGSEGV ani SIGBUS
Funkcje
• int pthread sigmask(int how, const sigset t *set, sigset t *oset)
– how określa jak zbiór set wpływa na bieżący zbiór
∗ SIG BLOCK — włączenie wskazanych sygnałów do zbioru∗ SIG UNBLOCK — odblokowanie wskazanych sygnałów∗ SIG SETMASK — zastąpienie bieżącego zbioru nowym
– gdy oset nie jest pusty, poprzednia maska sygnałów zapisywana jest pod wskazywanym adre-sem
3.4.2 Wysyłanie sygnałów do wątków
Wysyłanie sygnału do określona wątku umożliwia funkcja pthread kill. Jeśli numer sygnału jestrówny zero, wówczas nie jest wysyłany sygnał, ale są testowane jedynie ewentualne błędy — a więc czywskazany identyfikator wątku jest poprawny.
UWAGA
Sygnały, które dotyczą procesów, lecz zostaną wysłane do wątku nie zmieniająswojego znaczenia. Np. wysłanie SIGSTOP zatrzyma proces, a nie wątek.
Funkcje
• int pthread kill(pthread t id, int signum)
– id — wątek
– signum — numer sygnału
3.4.3 Przykład
W przykładowym programie wątek główny czeka na sygnał SIGUSR1, który po pewnym czasie wysyłautworzony wcześniej wątek.
⇒ Przejdź do przykładowego programu nr 13.
Wyjście:
$ ./przykladwątek główny oczekuje na sygnałwątek się rozpocząłwątek wysyła sygnał SIGUSR1 do głównego wątkuwątek główny otrzymał sygnał SIGUSR1
3.5 Przerywanie wątków
Wskazany wątek może zostać przerwany, jeśli tylko nie zostało to wprost zabronione. Sygnał przerwa-nia wysyła funkcja pthread cancel.
Sposób przerwania wątku jest dwojaki:
1. Asynchroniczny — przerwanie następuje natychmiast, w dowolnym momencie.
2. Opóźniony (deferred) — przerwanie następuje dopiero po osiągnięciu tzw. punktu przerwa-nia (cancellation point), tj. wywołania określonych funkcji systemowych (np. sleep, read).Standard POSIX określa, które funkcje muszą, a które mogą być punktami przerwania, defi-niuje także dodatkowo pthread testcancel(void). Pełna lista funkcji znajduje się w rozdzia-le [http://www.opengroup.org/onlinepubs/000095399/functions/xsh chap02 09.html 2.9.5 ThreadCancellation]);
Ustawienie flagi kontrolującej możliwość przerwania wątku wykonuje funkcja pthread setcancelstate,akceptuje dwie wartości:
• PTHREAD CANCEL DISABLE — przerwanie niemożliwe; jeśli jednak wystąpi żądanie przerwania, faktten jest pamiętany i gdy stan flagi zmieni się na PTHREAD CANCEL ENABLE wątek zostanie prze-rwany.
Wybór sposobu przerywania umożliwia funkcja pthread setcanceltype, która akceptuje dwie wartości:
Biblioteka zarządza trzema listami funkcji, które są wykonywane przy forkowaniu procesu. Jedna listaprzechowuje adresy funkcji wywoływanych przed właściwym uruchomieniem funkcji fork, dwie kolejnezawierają funkcje wykonywane tuż po zakończeniu fork, osobno w procesie rodzica i potomnym.
Funkcja pthread atfork służy do dodawania do list funkcji użytkownika; można podawać pustewskaźniki.
Funkcje wykonywane po forku są wykonywane w kolejności zgodnej z dodawaniem ich do list, nato-miast przed forkiem są uruchamiane w kolejności odwrotnej.
Zastosowaniem tego mechanizmu może być ponowna inicjalizacja wątków w procesie potomnym.Przede wszystkim przy forkowaniu w procesie potomnym działa tylko jeden wątek — główny wątek, wy-konujący funkcję main. Ponadto konieczna jest ponowna inicjalizacja różnych obiektów synchronizujących(np. mutexów), bowiem — jak wspomniano we wstępie — samo skopiowanie pamięci jest niewystarcza-jącego do utworzenia w pełni funkcjonalnej kopii takiego obiektu.
3.6.1 Funkcje
• int pthread atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
– prepare — funkcja wykonywana przed fork()
– parent — funkcja wykonywana po fork() w procesie rodzica
– child — funkcja wykonywana po fork() w procesie potomnym
3.6.2 Przykład
W programie proces potomny odtwarza jeden działający wątek.
⇒ Przejdź do przykładowego programu nr 18.
Wyjście:
$ ./przykladpoczątek programutworzenie 3 wątków w procesie 8228uruchomiono wątek #1wątek #1 w procesie #8228uruchomiono wątek #2wątek #2 w procesie #8228uruchomiono wątek #3wątek #3 w procesie #8228wątek #1 w procesie #8228wątek #2 w procesie #8228wątek #3 w procesie #8228fork => 8232tworzenie 3 wątków w procesie 8232fork => 0uruchomiono wątek #1wątek #1 w procesie #8232uruchomiono wątek #2wątek #2 w procesie #8232uruchomiono wątek #3wątek #3 w procesie #8232wątek #1 w procesie #8228wątek #3 w procesie #8228wątek #2 w procesie #8228
wątek #1 w procesie #8232wątek #2 w procesie #8232wątek #3 w procesie #8232wątek #1 w procesie #8228wątek #2 w procesie #8228wątek #3 w procesie #8228wątek #1 w procesie #8232wątek #2 w procesie #8232wątek #3 w procesie #8232wątek #1 w procesie #8228wątek #3 w procesie #8228wątek #2 w procesie #8228
3.7 Stopień współbieżności
Dostępne jeśli biblioteka implementuje opcję XSI .Stopień współbieżności jest podpowiedzią (hint) dla biblioteki i ma znaczenie, jeśli wątki pthreads
są uruchamiane na mniejszej liczbie wątków systemowych. Wówczas podpowiedź określa na ilu rzeczywi-stych wątkach zależy programowi.
W Linuxie jeden wątek pthreads odpowiada jednemu wątkowi systemowemu, więc ten parametr niema żadnego znaczenia.
3.7.1 Funkcja
• int pthread setconcurrency(int new level)
– ustawia nowy stopień współbieżności; jeśli 0, przyjmowany jest domyślny
• int pthread getconcurrency(void)
– odczyt
3.8 Czas procesora zużyty przez wątek
Jeśli system implementuje timery (TMR) i rozszerzenie pthreads (TCT) dostępna jest funkcjapthread getcpuclockid, zwracająca identyfikator zegara, który odmierza czas procesora zużyty przezwątek. Zdefiniowana jest wówczas także stała w time.h CLOCK THREAD CPUTIME ID, która odpowiadaidentyfikatorowi zegara dla wywołującego wątku.
Makrodefinicja POSIX THREAD CPUTIME informuje o istnieniu rozszerzenia.Do odczytania czasu na podstawie identyfikatora zegara służy funkcja clock gettime (z time.h).
3.8.1 Funkcja
• int pthread getcpuclockid(pthread t id, clockid t *clock id)
– odczyt identyfikatora zegara
3.8.2 Szkic użycia
\begin{enumerate}
\item include <pthread.h>\item include <time.h>\end{enumerate}
\item else\item error "pthread_getcpuclockid niedostępne w tym systemie"\item endif\end{enumerate}
3.8.3 Przykład
⇒ Przejdź do przykładowego programu nr 14.
Wynik na maszynie dwuprocesorowej:
$ ./przykladpoczątek programu, uruchomianie zostanie 10 wątkówwątek #0 uruchomiony, dwa razy wykona 86832212 pustych pętliwątek #7 uruchomiony, dwa razy wykona 8298184 pustych pętliwątek #9 uruchomiony, dwa razy wykona 67891648 pustych pętliwątek #5 uruchomiony, dwa razy wykona 27931234 pustych pętliwątek #8 uruchomiony, dwa razy wykona 23876946 pustych pętliwątek #3 uruchomiony, dwa razy wykona 52231547 pustych pętliwątek #6 uruchomiony, dwa razy wykona 16183104 pustych pętliwątek #4 uruchomiony, dwa razy wykona 87068047 pustych pętliwątek #1 uruchomiony, dwa razy wykona 59202170 pustych pętliwątek #2 uruchomiony, dwa razy wykona 48470151 pustych pętlipo około sekundzie wątki zużyły:* #0: 209ms* #1: 138ms* #2: 119ms* #3: 125ms
27
3.8. CZAS PROCESORA ZUŻYTY PRZEZ WĄTEK
* #4: 209ms* #5: 67ms* #6: 41ms* #7: 17ms* #8: 59ms* #9: 154mswątek #7 zakończony, zużył 39ms czasu procesorawątek #6 zakończony, zużył 80ms czasu procesorawątek #8 zakończony, zużył 117ms czasu procesorawątek #5 zakończony, zużył 135ms czasu procesorawątek #2 zakończony, zużył 232ms czasu procesorawątek #3 zakończony, zużył 251ms czasu procesorawątek #1 zakończony, zużył 285ms czasu procesorawątek #9 zakończony, zużył 323ms czasu procesorawątek #0 zakończony, zużył 423ms czasu procesorawątek #4 zakończony, zużył 418ms czasu procesora
główny wątek zużył 0ms czasu procesoraproces zużył 2307ms czasu procesora
28
Rozdział 4
Synchronizacja
29
Pthreads udostępnia kilka sposobów synchronizacji między wątkami:
• podstawowe
– Mutexy (blokada na wyłączność)
– Zmienne warunkowe (condition variable)
– Oczekiwanie na zakończenie wątku
• opcjonalne
– Blokady zapis/odczyt (rwlock)
– Bariery
– Wirujące blokady (spinlock)
Na tej liście nie ma semaforów, ponieważ zostały zdefiniowane we wcześniejszej wersji standardu POSIX —właściwie istniały wcześniej, jako jeden ze standardowych mechanizmów IPC .
Jeśli biblioteka implementuje opcję TSH (Thread Process-Shared Synchronization), wówczas możliwastaje się synchronizacja między wątkami różnych procesów przy użyciu mutexów, zmiennych warunko-wych, blokad odczyt/zapis i barier.
Mutex (MUTual EXclusion, wzajemne wykluczanie) jest blokadą, którą może uzyskać tylko jedenwątek. Mutexy służą głównie do realizacji sekcji krytycznych , czyli bezpiecznego w sensie wielowątkowymdostępu do zasobów współdzielonych.
Schemat działania na mutexach jest następujący:
1. pozyskanie blokady
2. modyfikacja lub odczyt współdzielonego obiektu
3. zwolnienie blokady
Mutex w pthreads jest opisywany przez strukturę typu pthread mutex t, zaś jego atrybutypthread mutexattr t.
4.1.1 Inicjalizacja i zwalnianie mutexu
Zmienna typu pthread mutex t może zostać zainicjowana na dwa sposoby:
• poprzez przypisanie symbolu PTHREAD MUTEX INITIALIZER;
• przez wywołanie funkcji pthread mutex init, która umożliwia również podanie atrybutów blokady.
Każdy mutex, bez względu na sposób inicjalizacji, musi zostać zwolniony funkcjapthread mutex destroy. Implementacja biblioteki może bowiem PTHREAD MUTEX INITIALIZER realizo-wać poprzez wywołanie jakiejś funkcji, która np. alokuje pamięć i nie zwolnienie mutexu doprowadzi dowycieku pamięci.
Typy
• pthread mutex t
– mutex
Funkcje
• int pthread mutex create(pthread mutex t *mutex, const pthread mutexattr t *attr)
– inicjacja mutexa, wskaźnik na atrybuty attr może być pusty
• int pthread mutex destroy(pthread mutex t *mutex)
Jeśli żaden inny wątek nie posiada blokady, działają identycznie — tzn. blokada jest przyznawana wywo-łującemu wątkowi. Różnią się zachowaniem w przypadku niemożności uzyskania blokady:
1. pthread mutex lock — oczekiwanie w nieskończoność, aż blokada zostanie zwolniona przez innywątek;
3. pthread mutex timedlock — oczekiwanie ograniczone czasowo, jeśli czas minie, zwraca kodETIMEDOUT.
Wątek musi zwolnić blokadę funkcją pthread unlock.Funkcja pthread mutex timedlock jest dostępna, gdy system implementuje rozszerzenie TMO. W od-
różnieniu od innych funkcji operujących na czasach oczekiwania (np. select dla plików), w których podajesię ile czasu ma upłynąć od chwili wywołania funkcji, w pthreads podawany jest czas bezwzględny.
Funkcje
• int pthread mutex lock(pthread mutex t *mutex)
• int pthread trylock(pthread mutex t *mutex)
• int pthread timedlock(pthread mutex t *mutex, const struct timespec *timeout)
/* ... */struct timespec timeout;clock_gettime(CLOCK_REALTIME, &timeout); // pobranie bieżącego czasutimeout.tv_sec += 2; // zwiększenie liczby sekund o 2
switch (pthread_mutex_timedlock(&mutex, &timeout)) {case 0:puts("Blokada pozyskana przed upływem 2 sekund");// działania na obiekcie współdzielonympthread_mutex_unlock(&mutex);break;
case ETIMEDOUT:puts("Upłynęły 2 sekundy");break;
default:puts("Inny błąd");break;}
Przykład
Program demonstruje sekcję krytyczną z użyciem mutexów. Jeśli przy kompilacji zdefiniowane zo-stanie BLOKADA, wówczas mutex blokuje dostęp do zmiennej, która jest inkrementowana określoną liczbęrazy przez każdy z wątków. W przeciwnym razie wątki zmieniają ją bez żadnej synchronizacji, co możeprowadzić do błędu — w tym przypadku do niepoprawnego zliczenia.
Można wyobrazić sobie sytuację (raczej prawdopodobną), gdy w programie istnieje funkcja pomocni-cza, wykorzystywana przez wątki, która zakłada blokadę na pewne dane. Problem pojawia się w chwili,gdy wątek już pozyskał blokadę i wywołuje taką funkcję. Wówczas z punktu widzenia blokady wątekpróbuje wykonać następującą sekwencję:
pthread_mutex_lock(&mutex); // (1)pthread_mutex_lock(&mutex); // (2) - w funkcji pomocniczej/* ... */pthread_mutex_unlock(&mutex) // (3) - w funkcji pomocniczejpthread_mutex_unlock(&mutex) // (4)
• W przypadku mutexu zwykłego wykona się pierwsza funkcja pthread mutex lock (1), zaś nadrugim jej wywołaniu (2) wątek zatrzyma się, oczekując na zwolnienie blokady — co nigdy nienastąpi, bowiem sterowanie nie dojdzie do wiersza (3) ani (4). Występuje zakleszczenie.
• W przypadku mutexu rekursywnego wykonają się wszystkie funkcje związane z blokadą. Mu-tex tego typu posiada dodatkowy licznik zagnieżdżeń, który z każdym wywołaniem funkcjipthrad mutex lock jest zwiększany, natomiast wywołanie pthread mutex unlock zmniejsza go —gdy osiągnie zero, blokada jest zwalniana.
• W przypadku mutexu bezpiecznego drugie wywołanie pthread mutex lock zwróci kod błęduEDEADLK, oznaczający, że wątek już posiada tę blokadę.
Ponowne odblokowanie
Jeśli blokada jest zwolniona ponowne wywołanie pthread unlock mutexy bezpieczne i rekursywnyzwracają błąd. Zachowanie zwykłego mutexu jest nieokreślone!
Przykład
Ilustracja różnicy w działaniu mutexów.
⇒ Przejdź do przykładowego programu nr 8.
• Wynik dla mutexu zwykłego — wystąpiło zakleszczenie, program ”zawiesił się” i musiał zostaćprzerwany ręcznie:
$ ./przyklad 0mutex typu PTHREAD_MUTEX_NORMALprzed wykonaniem pthread_mutex_lock (1)... wykonano pthread_mutex_lock (1)przed wykonaniem pthread_mutex_lock (2)\textbf{^C}
• Wynik dla mutexu sprawdzającego — nie dopuszczono do zakleszczenia:
$ ./przyklad 1mutex typu PTHREAD_MUTEX_ERRORCHECKprzed wykonaniem pthread_mutex_lock (1)... wykonano pthread_mutex_lock (1)przed wykonaniem pthread_mutex_lock (2)\textbf{pthread_mutex_lock (2): Resource deadlock avoided}
• Wynik dla mutexu rekursywnego — blokada jest pozyskiwana wielokrotnie:
34
4.1. MUTEXY
$ ./przyklad 2mutex typu PTHREAD_MUTEX_RECURSIVEprzed wykonaniem pthread_mutex_lock (1)... wykonano pthread_mutex_lock (1)przed wykonaniem pthread_mutex_lock (2)... wykonano pthread_mutex_lock (2)przed wykonaniem pthread_mutex_unlock (2)... wykonano pthread_mutex_unlock (2)przed wykonaniem pthread_mutex_unlock (1)... wykonano pthread_mutex_unlock (1)program zakończony
4.1.4 Atrybuty mutexu
Inicjalizacja i usuwanie
Typy
• pthread mutexattr t
Funkcje
• int pthread mutexattr destroy(pthread mutexattr t *attr)
• int pthread mutexattr init(pthread mutexattr t *attr)
Typ mutexu
Opisane wyżej
Funkcje
• int pthread mutexattr settype(pthread mutexattr t *attr, int type)
• int pthread mutexattr gettype(const pthread mutexattr t *atter, int *type)
Współdzielenie mutexu z innymi procesami
Patrz rozdział synchronizacja między wątkami różnych procesów.
Funkcje
• int pthread mutexattr setpshared(pthread mutexattr t *attr, int pshared)
• int pthread mutexattr getpshared(const pthread mutexattr t *attr, int *pshared)
Zmiana priorytetu wątku posiadającego blokadę
Dostępne, gdy istnieje rozszerzenie TPP (oraz TPI).Wartość atrybutu decyduje o strategii wykonywania programu, gdy wiele wątków o różnych prioryte-
tach stara się o uzyskanie blokady. Atrybut może mieć wartości:
W przypadku PTHREAD PRIO NONE priorytet wątku, który pozyskuje blokadę nie zmienia.W dwóch pozostałych przypadkach z mutexem powiązany zostaje pewien priorytet i gdy wątek uzyska
blokadę, wówczas jego priorytet jest podbijany do wartość z mutexu (o ile oczywiście był wcześniej niższy).Innymi słowy w obrębie sekcji krytycznej wątek może działać z wyższym priorytetem.
Sposób ustalania priorytetu mutexu zależy od atrybutu:
• PTHREAD PRIO INHERIT — wybierany jest maksymalny priorytet spośród wątków oczekujących nauzyskanie danej blokady;
• PTHREAD PRIO PROTECT — priorytet jest ustalany przez programistę funkcjąpthread mutexattr setprioceiling lub pthread mutex setprioceiling (opisane w następnejsekcji).
Dodatkowo jeśli wybrano wartość PTHREAD PRIO PROTECT, wówczas wszelkie próby założenia blokadyfunkcjami pthread mutex XXXlock z poziomu wątków o priorytecie niższym niż ustawiony dla mutexanie powiodą się — zostanie zwrócona wartość błędu EINVAL.
Funkcje
• int pthread mutexattr setprotocol(pthread mutexattr t *attr, int protocol)
• int pthread mutexattr getprotocol(const pthread mutexattr t *attr, int *protocol)
Minimalny priorytet wątku zakładające blokadę
Dostępne w opcji TPP. Funkcje ustalają/odczytują bieżący priorytet związany z mutexem.
Funkcje działające na atrybutach
• int pthread mutexattr setprioceiling(pthread mutexattr t *attr, int prioceiling)
• int pthread mutexattr getprioceiling(const pthread mutexattr t *attr, int *prioceiling)
Funkcje działające bezpośrednio na mutexie
• int pthread mutex setprioceiling(pthread mutex t *attr, int prioceiling)
• int pthread mutex getprioceiling(const pthread mutex t *attr, int *prioceiling)
Zmienna warunkowa (condition variable) jest jednym ze sposobów synchronizacji między wątka-mi — polega na przesłaniu sygnału z jednego wątku do innych wątków, które na ten sygnał oczekują.
Prościej rzecz ujmując jeden lub kilka wątków może oczekiwać na zajście jakiegoś warunku, innywątek, gdy go spełni, sygnalizuje właśnie poprzez zmienną warunkową ten fakt jednemu lub wszystkimoczekującym. We wzorcu producent-konsument występuje właśnie taka sytuacja: konsument (jeden lubwięcej) czeka na pojawienie się obiektów od producenta (jednego lub więcej).
Zmienna warunkowa jest zawsze używana z mutexem.Typem danych, który opisuje zmienną warunkową jest pthread cond t.
4.3 Schemat użycia zmiennej warunkowej podczas oczekiwania
1. pozyskaj blokadę (mutex)
2. sprawdź, czy warunek zaszedł
3. jeśli tak, zwolnij blokadę
4. w przeciwnym razie oczekuj na sygnał (w tym miejscu blokada jest automatycznie zwalniana)
5. * sygnał nadszedł, przejdź do punktu 2 (w tym miejscu blokada jest automatycznie ponowniepozyskiwana)
Co w języku C wygląda mniej więcej tak:
pthread_mutex_lock(&mutex)do {if (warunek spełniony) {/* ... */break;}elsepthread_cond_wait(&cond, &mutex);} while (1)pthread_mutex_unlock(&mutex);
4.4 Inicjalizacja zmiennej warunkowej
Funkcją pthread cond init inicjuje zmienną warunkową, umożliwia również przypisanie atrybutów.Istnieje możliwość statycznej inicjalizacji wartością PTHREAD COND INITIALIZER.
Każda zmienna warunkowa, bez względu na sposób inicjalizacji, musi zostać zwolniony funkcjapthread cond destroy. Implementacja biblioteki może bowiem PTHREAD COND INITIALIZER realizowaćpoprzez wywołanie jakiejś funkcji, która np. alokuje pamięć i nie zwolnienie zmiennej doprowadzi dowycieku pamięci.
wątek #2 oczekuje na sygnał...pthread_cond_signal - sygnalizacja... wątek #5 otrzymał sygnał!wątek #5 oczekuje na sygnał...pthread_cond_broadcast - rozgłaszanie... wątek #3 otrzymał sygnał!wątek #3 oczekuje na sygnał...... wątek #1 otrzymał sygnał!wątek #1 oczekuje na sygnał...... wątek #4 otrzymał sygnał!wątek #4 oczekuje na sygnał...... wątek #2 otrzymał sygnał!wątek #2 oczekuje na sygnał...... wątek #5 otrzymał sygnał!wątek #5 oczekuje na sygnał...koniec programu
40
4.9. WSTĘP
4.9 Wstęp
Mutexy czy wirujące blokady umożliwiają ochronę współdzielonych zasobów jednakowo traktującwątki zmieniające ten obiekt jak i wątki jedynie czytające — dopuszczając do obiektu tylko jeden wątek.
Blokady zapis/odczyt (rwlocks) rozróżniają cel dostępu do obiektu współdzielonego — wątek możezałożyć blokadę do odczytu (rdlock) lub do zapisu (wrlock). Dowolna liczba wątków może mieć jedno-czesny dostęp do obiektu chronionego, jeśli tylko zakładają blokadę do odczytu, natomiast dokładniejeden wątek ma dostęp do obiektu, gdy założy blokadę do zapisu.
4.10 Inicjalizacja i destrukcja
Blokadę tworzy funkcja pthread rwlock init, natomiast niszczy pthread rwlock destroy.
4.10.1 Typy
• pthread rwlock t
• pthread rwlockattr t
4.10.2 Funkcje
• pthread rwlock init
• pthread rwlock destroy
4.11 Atrybuty blokady
Atrybuty blokady tworzy funkcja pthread rwlockattr init, natomiast niszczypthread rwlockattr destroy.
Jeśli biblioteka implementuje opcję THS, wówczas blokada ma tylko jeden parametr: flagęwspółdzielenia między procesami, którą ustawia pthread rwlockattr setpshared, zaś odczytujepthread rwlockattr getpshared.
4.11.1 Typy
• pthread rwlockattr t
4.11.2 Funkcje
• pthread rwlockattr init(pthread rwlockattr t *attr)
– incjalizacja
• pthread rwlockattr destroy(pthread rwlockattr t *attr)
– zniszczenie
• pthread rwlockattr setpshared(pthread rwlockattr t *attr, int pshared)
– ustawienie flagi, pshared ma wartość PTHREAD PROCESS PRIVATE lubPTHREAD PROCESS SHARED
• pthread rwlockattr getpshared(const pthread rwlockattr t *attr, int pshared
– oczekiwanie na pozyskanie blokady ograniczone czasowo (dostępne jeśli biblioteka implemen-tuje opcję TMO)
4.14 Zwalnianie blokady
Niezależnie od rodzaju pozyskanej blokady, wątek zwalnia ją funkcją pthread rwlock unlock.
4.14.1 Funkcje
• int pthread rwlock unlock(pthread rwlock t *rwlock)
4.15 Przykład
Przykładowy program tworzy kilka wątków piszących, a więc zakładających blokady do zapisu, orazwięcej wątków czytających, zakładających blokady do odczytu.
⇒ Przejdź do przykładowego programu nr 16.
Przykładowy wynik:
$ ./przykladpisarz #0 czeka na dostęppisarz #0 ustawia nową wartośćpisarz #1 czeka na dostępczytelnik #0 czeka na dostępczytelnik #1 czeka na dostępczytelnik #3 czeka na dostęp
czytelnik #2 czeka na dostępczytelnik #4 czeka na dostęppisarz #0 zwalnia blokadępisarz #1 ustawia nową wartośćpisarz #1 zwalnia blokadęczytelnik #0 odczytuje wartośćczytelnik #4 odczytuje wartośćczytelnik #1 odczytuje wartośćczytelnik #3 odczytuje wartośćczytelnik #2 odczytuje wartośćczytelnik #4 zwalnia blokadęczytelnik #1 zwalnia blokadęczytelnik #2 zwalnia blokadęczytelnik #0 zwalnia blokadęczytelnik #3 zwalnia blokadęczytelnik #3 czeka na dostępczytelnik #3 odczytuje wartośćczytelnik #0 czeka na dostępczytelnik #0 odczytuje wartośćczytelnik #2 czeka na dostępczytelnik #2 odczytuje wartośćczytelnik #1 czeka na dostępczytelnik #1 odczytuje wartośćczytelnik #4 czeka na dostępczytelnik #4 odczytuje wartośćczytelnik #2 zwalnia blokadęczytelnik #3 zwalnia blokadęczytelnik #4 zwalnia blokadęczytelnik #0 zwalnia blokadęczytelnik #1 zwalnia blokadępisarz #0 czeka na dostęppisarz #0 ustawia nową wartośćczytelnik #2 czeka na dostępczytelnik #3 czeka na dostępczytelnik #4 czeka na dostępczytelnik #1 czeka na dostępczytelnik #0 czeka na dostęppisarz #0 zwalnia blokadęczytelnik #2 odczytuje wartośćczytelnik #4 odczytuje wartośćpisarz #1 czeka na dostępczytelnik #0 odczytuje wartośćczytelnik #3 odczytuje wartośćczytelnik #1 odczytuje wartośćczytelnik #1 zwalnia blokadęczytelnik #3 zwalnia blokadęczytelnik #0 zwalnia blokadęczytelnik #2 zwalnia blokadęczytelnik #4 zwalnia blokadępisarz #1 ustawia nową wartość
43
4.16. WSTĘP
4.16 Wstęp
Bariera jest mechanizmem synchronizacji grupy wątków. Wątki po dojściu bo bariery są wstrzymy-wane do czasu, aż ostatni jej nie osiągnie — wówczas wszystkie są kontynuowane. Z barierą związana jestliczba większa od zera określająca ile wątków wchodzi w skład grupy.
Gdy bariera jest osiągana przez wszystkie wątki, jej stan (licznik) jest automatycznie inicjowany, nataką wartość, jaką ustawiło ostatnie wywołanie pthread barrier init.
4.17 Inicjalizacja bariery
Inicjalizację bariery wykonuje funkcja pthread barrier init, usuwa zaś funkcjapthread barrier destroy. Bariera może mieć dodatkowe atrybut.
4.17.1 Typy
• pthread barrier t
– bariera
• pthread barrierattr t
– atrybuty bariery
4.17.2 Funkcje
• int pthread barrier init(pthread barrier t *barrier, pthread barrierattr t* attr,unsigned int count)
– inicjacja bariery, count jest liczbą wątków w grupie, attr opcjonalne atrybuty
• int pthread barrier destroy(pthread barrier t *barrier)
– usunięcie bariery
4.18 Atrybuty bariery
Gdy biblioteka implementuje opcję TSH , wówczas można ustalić, czy bariery mogą być współdzielonemiędzy procesami — domyślnie nie.
4.18.1 Funkcje
• int pthread barrier init(pthread barrierattr t *attr)
– inicjacja atrybutów bariery
• int pthread barrierattr destroy(pthread barrierattr t *attr)
– usunięcie atrybutów
• int pthread barrierattr setpshared(pthread barrierattr t *attr, int pshared)
– ustawienie flagi współdzielenia z innymi procesami; pshared ma wartośćPTHREAD PROCESS SHARED lub PTHREAD PROCESS PRIVATE (domyślnie)
• int pthread barrierattr getpshared(pthread barrierattr t *attr, int *pshared)
Wywołanie funkcji pthread barrier wait jest traktowane jako dojście do bariery — powoduje zwięk-szenie licznika związanego z barierą.
Gdy bariera zostanie osiągnięta przez wszystkie wątki, funkcja w jednym wątku (standard nie określaw którym) zwraca specjalną wartość PTHREAD BARRIER SERIAL THREAD, pozostałe wartość 0.
4.19.1 Funkcje
• int pthread barrier wait(pthread barrier t *barrier)
4.19.2 Przykład
W programie bariera służy do wstrzymania programu, do czasu aż wszystkie utworzone wątki skończądziałanie.
⇒ Przejdź do przykładowego programu nr 11.
Przykładowe wyjście:
zostanie uruchomionych 10 wątkówwątek #0 rozpoczęty, zostanie wstrzymany na 0 sekundwątek #0 osiągnął barieręwątek #1 rozpoczęty, zostanie wstrzymany na 0 sekundwątek #1 osiągnął barieręwątek #2 rozpoczęty, zostanie wstrzymany na 0 sekundwątek #2 osiągnął barieręwątek #3 rozpoczęty, zostanie wstrzymany na 0 sekundwątek #3 osiągnął barieręwątek #4 rozpoczęty, zostanie wstrzymany na 4 sekundwątek #5 rozpoczęty, zostanie wstrzymany na 4 sekundwątek #6 rozpoczęty, zostanie wstrzymany na 3 sekundwątek #7 rozpoczęty, zostanie wstrzymany na 4 sekundwątek #8 rozpoczęty, zostanie wstrzymany na 4 sekundwątek #9 rozpoczęty, zostanie wstrzymany na 4 sekundwątek główny osiągnął barieręwątek #6 osiągnął barieręwątek #4 osiągnął barieręwątek #5 osiągnął barieręwątek #7 osiągnął barieręwątek #8 osiągnął barieręwszystkie wątki osiągnęły barierę (wątek #9)
Wirująca blokada (spinlock) jest rodzajem blokady, w której aktywnie oczekuje się na jej zwolnieniew przypadku, gdy nie może zostać pozyskana. Pod względem semantyki nie różnią się od mutexów, jednakzasadniczo różnią w implementacji i zakresie zastosowania.
Przede wszystkim spinlock zużywa czas procesora, więc jego stosowanie ma sens, gdy współdzielonezasoby nie są zbyt długo blokowane przez inne wątki. Ponadto na systemach wieloprocesorowych unikasię kosztownego przełączania kontekstu.
4.21 Inicjalizacja i zwalnianie
4.21.1 Typy
• pthread spinlock t
4.21.2 Funkcje
• int pthread spin init(pthread spinlock t *lock, int pshared)
– inicjalizacja wirującej blokady; argument pshared przymuje wartościPTHREAD PROCESS PRIVATE lub PTHREAD PROCESS SHARED, jeśli dostępna jest opcja THS —patrz synchronizacja między wątkami różnych procesów
• int pthread spin destroy(pthread spinlock t *lock)
– zniszczenie wirującej blokady
4.22 Pozyskiwanie blokady
4.22.1 Funkcje
• int pthread spin lock(pthread spinlock t *lock)
– założenie blokady, jeśli nie jest to możliwe — oczekiwanie
• int pthread spin trylock(pthread spinlock t *lock)
– założenie blokady, jeśli nie jest to możliwe — zwrócenie kodu błędu EBUSY
4.23 Zwalnianie blokady
4.23.1 Funkcje
• int pthread spin unlock(pthread spinlock t *lock)
Jeśli biblioteka pthreads implementuje opcję TSH (Thread Process-Shared Synchronization), wów-czas możliwa staje się synchronizacja między wątkami różnych procesów przy użyciu:
• mutexów,
• zmiennych warunkowych,
• blokad odczyt/zapis,
• barier.
Domyślnie obiekty synchronizujące są lokalne względem procesu, w ramach którego zostały utworzone.Jeśli są współdzielone z innymi procesami, wówczas można używać adresów obiektów znajdujących sięw segmencie pamięci dzielonej.
UWAGA
Standard nie określa, co się stanie jeśli prywatne obiekty synchronizujące zo-staną użyte przez wątki innego procesu. Np. w Linuxie nie są zgłaszane żadnebłędy, lecz synchronizacja nie funkcjonuje.
To, czy obiekt synchronizujący będzie współdzielony decydują jego atrybuty — ustawiane funkcja-mi int pthread XXXattr setpshared(pthread XXXattr t *attr, int shared) (XXX=mutex, cond,rwlock, barrier), gdzie parametr shared przyjmuje wartości:
• PTHREAD PROCESS PRIVATE (domyślnie) — obiekt prywatny;
• PTHREAD PROCESS SHARED — współdzielony.
4.24 Przykład
Przykładowy program w zależności od argumentów:
1. Tworzy segment pamięci dzielonej, podłącza go do przestrzeni adresowej procesu, w którego obszarzeinicjalizuje mutex i zmienną warunkową na współdzielone (PTHREAD PROCESS SHARED) i oczekuje nawarunek — ustawienie napisu.
2. Podłącza się do już utworzonego segmentu pamięci, ustawia napis i sygnalizuje zmienną warunkowąten fakt.
⇒ Przejdź do przykładowego programu nr 17.
$ ./przykladproces 1id segmentu pamięci dzielonej: 27197465adres przyłączonego segmentu: 0xb76e7000pthread_cond_wait
inny proces ustawił napis: ’czy konie mnie słyszą?’$
47
4.24. PRZYKŁAD
$ ./przyklad 27197465 "czy konie mnie słyszą?"proces 2: segment pamięci dzielonej 27197465adres przyłączonego segmentu: 0xb7727000proces ustawił napis ’czy konie mnie słyszą?’ i wykonał pthread_cond_signal$
48
Rozdział 5
Rozszerzenia
49
5.1. LINUX
5.1 Linux
Sufiksem nazw większości funkcji Linuxa jest np.
5.1.1 Zbiór procesorów, na jakich może uruchomić się wątek
Funkcje umożliwiają ustawienie i odczyt zbioru procesorów na jakich wątek ma działać. (Analogiczneustawienia są możliwe na poziomie procesów).
• pthread setaffinity np (3)
• pthread getaffinity np (3)
• pthread attr getaffinity np (3)
• pthread attr setaffinity np (3)
5.1.2 Funkcje finalizujące i asynchroniczne przerwania
• pthread cleanup push defer np (3) — funkcja działa podobnie, jak pthread cleanup push, z tymże po odłożeniu funkcji na stos ustawia sposób przerwania wątku na opóźniony (jednocześnie zapa-miętując bieżące ustawiania)
• pthread cleanup pop restore np (3) — ściąga za stosu funkcję i ewentulanie uruchamia, odtwarzapoprzedni sposób przerywania
5.1.3 Bieżące atrybuty wątku
Umożliwia odczytanie bieżących atrybutów już uruchomionego wątku.
• pthread getattr np (3)
5.1.4 Oczekiwania na zakończenie wątku
Uzupełnienie mechanizmu oczekiwania na zakończenie wątków (pthread join):
Niektóre przykłady (wirujące blokady, bariery) muszą być linkowane z librt, czyli konieczna jest opcja-lrt.
Aby skompilować część z nich trzeba ustawić odpowiednie definicje preprocesora, w szczególnościPOSIX C SOURCE na odpowiednią wartość — w przykładach jest to 200809L, dając dostęp do funkcji z
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : h e l l o world z k i l k u wątków∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
/∗ f u n k c j a wykonywana w wątku − nic s p e c j a l n e g o nie r o b i ∗/void∗ watek ( void∗ arg ) {
puts ( ”Witaj w równoległym ś w i e c i e ! ” ) ;return NULL;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : zakończen ie wątku z poziomu f u n k c j i wywływanych w wątku∗ za pomocą∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
void koniec watku ( int l i c z n i k , int l i m i t ) {int i ;for ( i =0; i < l i c z n i k ; i++) putchar ( ’ ’ ) ;p r i n t f ( ” l i c z n i k = %d , l i m i t = %d\n” , l i c z n i k , l i m i t ) ;
i f ( l i c z n i k == l i m i t )/∗ zakończen ie wątku w k t ó r e g o k o n t e k ś c i e wykonywana j e s t ta f u n k c j a ∗/p t h r e a d e x i t (NULL) ;
elsekoniec watku ( l i c z n i k +1, l i m i t ) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : przekazywanie parametrów do f u n k c j i wątku i zwracanie wyników∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <pthread . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
typedef struct Arg { // s t r u k t u r a argumentów d l a wątku 1 .char napis [ 2 5 6 ] ;int rok ;int mies ;int dz ien ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : a t r y b u t y wątku − wypisanie domyślnych w a r t o ś c i∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include < l i m i t s . h> // PTHREAD STACK MIN#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
void wyswiet l a t rybuty ( const p t h r e a d a t t r t ∗ a t t r ) {int x ;s i z e t rozmiar ;void∗ addr ;struct sched param param ;
puts ( ” atrybuty wątku” ) ;
// r o d z a j wątkup r i n t f ( ”∗ rodza j : ” ) ;e r rno = p t h r e a d a t t r g e t d e t a c h s t a t e ( at t r , &x ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t d e t a c h s t a t e ” ) ;
// adres i rozmiar s t o s uerrno = p t h r e a d a t t r g e t s t a c k a d d r ( att r , &addr ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t s t a c k a d d r ” ) ;p r i n t f ( ”∗ adres s to su : %p\n” , addr ) ;
e r rno = p t h r e a d a t t r g e t s t a c k s i z e ( at t r , &rozmiar ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t s t a c k s i z e ” ) ;
63
6.2. WYKAZ PRZYKŁADÓW
p r i n t f ( ”∗ rozmiar s to su : %d ( minimalny %d) \n” , rozmiar , PTHREAD STACK MIN) ;
// rozmiar obszaru z a b e z p i e c z a j ą c e g o s t o s uerrno = p t h r e a d a t t r g e t g u a r d s i z e ( att r , &rozmiar ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t g u a r d s i z e ” ) ;p r i n t f ( ”∗ rozmiar obszaru zabezp i e c za j ą c ego : %d\n” , rozmiar ) ;
// szeregowanieerrno = p t h r e a d a t t r g e t i n h e r i t s c h e d ( att r , &x ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t i n h e r i t s c h e d ” ) ;switch ( x ) {
case PTHREAD INHERIT SCHED:puts ( ”∗ parametry szeregowania d z i e d z i c z o n e ” ) ;break ;
//p r i n t f ( ” − algorytm szeregowania : ” ) ;e r rno = p t h r e a d a t t r g e t s c h e d p o l i c y ( att r , &x ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t s c h e d p o l i c y ” ) ;switch ( x ) {
case SCHED OTHER:puts ( ”SCHED OTHER” ) ;break ;
case SCHED RR:puts ( ”SCHED RR” ) ;break ;
case SCHED FIFO:puts ( ”SCHED FIFO” ) ;break ;
default :puts ( ” ??? ” ) ;
}
//errno = pthread att r get schedparam ( att r , ¶m ) ;t e s t e r r n o ( ” pthread att r get schedparam ” ) ;p r i n t f ( ” − p r i o r y t e t : %d\n” , param . s c h e d p r i o r i t y ) ;break ;
default :puts ( ” ??? ” ) ;
}
// z a k r e s szeregowaniaerrno = p t h r e a d a t t r g e t s c o p e ( att r , &x ) ;t e s t e r r n o ( ” p t h r e a d a t t r g e t s c o p e ” ) ;
p r i n t f ( ”∗ zakre s szeregowania : ” ) ;switch ( x ) {
case PTHREAD SCOPE PROCESS:
64
6.2. WYKAZ PRZYKŁADÓW
puts ( ” proces ” ) ;break ;
case PTHREAD SCOPE SYSTEM:puts ( ” system” ) ;break ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : f u n k c j e f i n a l i z u j ą c e ( c leanup )∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>#include <uni s td . h> // s l e e p
#define t e s t e r r n o ( i n f o ) do { i f ( errno ) { per ro r ( i n f o ) ; e x i t (EXIT FAILURE);}} while (0 )
/∗ f u n k c j a f i n a l i z u j ą c a ∗/void zwo ln i j pamiec ( void∗ adres ) {
p r i n t f ( ” zwa ln ian i e pamięci spod adresu %p\n” , adres ) ;f r e e ( adres ) ;
void∗ watek ( void∗ id ) {char∗ t a b l i c a 1 = mal loc (100) ;char∗ t a b l i c a 2 = NULL;p r i n t f ( ”wątek #%d zaalokował 100 bajtów pod adresem %p\n” , ( int ) id ,
t a b l i c a 1 ) ;
pthread c leanup push ( zwoln i j pamiec , t a b l i c a 1 ) ;i f ( t a b l i c a 1 ) {
t a b l i c a 2 = mal loc (200) ;p r i n t f ( ”wątek #%d zaalokował 200 bajtów pod adresem %p\n” , ( int ) id ,
t a b l i c a 2 ) ;pthread c leanup push ( zwoln i j pamiec , t a b l i c a 2 ) ;
i f ( ( int ) id > 0)/∗ wątek s i ę kończy w tym punkcie , f u n k c j e f i n a l z u j ą c e
zos taną uruchomione ∗/p t h r e a d e x i t (NULL) ;
pthread c leanup pop (1) ;}
pthread c leanup pop (1) ;
p r i n t f ( ”wątek #%d zakończył s i ę \n” , ( int ) id ) ;return NULL;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : f u n k c j e wykonywane j e d n o k r o t n i e∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
/∗ o b i e k t gwarantujący jednokro tne wykonanie , musi z o s t a ć za in ic jowany ∗/pthread once t program gotowy = PTHREAD ONCE INIT;
void i n i c j a l i z a c j a ( ) {/∗ i n i c j a l i z a c j a , np . p r e k a l k u l o w a n i e j a k i ś t a b l i c ,
o t w i e r a n i e p l i k u logowania i t p . ∗/puts ( ” Rozpoczynanie programu” ) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : przerywanie wątków − program tworzy 3 wątk i z różnymi∗ ustawieniami dotyczącymi przerywania :∗ 1) dopuszcza przerwanie asynchroniczne∗ 2) dopuszcza przerwanie opóźnione∗ 3) p r z e z pewien czas w o g ó l e b l o k u j e przerwania∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <pthread . h>#include <uni s td . h> // pause w watek3#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
void zakonczen ie ( void∗ numer ) {p r i n t f ( ” funkc ja f i n a l i z u j ą c a dla wątku #%d\n” , ( int ) numer ) ;
e r rno = p t h r e a d s e t c a n c e l s t a t e (PTHREAD CANCEL ENABLE, NULL) ;t e s t e r r n o ( ” p t h r e a d s e t c a n c e l s t a t e ” ) ;
e r rno = pth r ead s e t canc e l t ype (PTHREAD CANCEL ASYNCHRONOUS, NULL) ;t e s t e r r n o ( ” p th r ead s e t canc e l t ype ” ) ;
p r i n t f ( ”\ turuchomiono wątek #%d ( przerwanie asynchron iczne ) \n” , ( int )numer ) ;
void∗ watek2 ( void∗ numer ) {int i , n ;pthread c leanup push ( zakonczenie , numer ) ;
e r rno = p t h r e a d s e t c a n c e l s t a t e (PTHREAD CANCEL ENABLE, NULL) ;t e s t e r r n o ( ” p t h r e a d s e t c a n c e l s t a t e ” ) ;
e r rno = pth r ead s e t canc e l t ype (PTHREAD CANCEL DEFERRED, NULL) ;t e s t e r r n o ( ” p th r ead s e t canc e l t ype ” ) ;
p r i n t f ( ”\ turuchomiono wątek #%d ( przerwanie opóźnione ) \n” , ( int ) numer ) ;while (1 ) {
p t h r e a d t e s t c a n c e l ( ) ; // punkt przerwanian = 1000000;for ( i =0; i < n ; i++)
void∗ watek3 ( void∗ numer ) {pthread c leanup push ( zakonczenie , numer ) ;e r rno = p t h r e a d s e t c a n c e l s t a t e (PTHREAD CANCEL DISABLE, NULL) ;t e s t e r r n o ( ” p t h r e a d s e t c a n c e l s t a t e ” ) ;
p r i n t f ( ”\ turuchomiono wątek #%d ( przez 2 sekundy n i e można przerwać ) \n” ,( int ) numer ) ;
s l e e p (2 ) ;
p r i n t f ( ”\ twątek #%d można już przerwać \n” , ( int ) numer ) ;e r rno = p t h r e a d s e t c a n c e l s t a t e (PTHREAD CANCEL ENABLE, NULL) ;t e s t e r r n o ( ” p t h r e a d s e t c a n c e l s t a t e ” ) ;pause ( ) ;
void przerwanie ( pthread t id , const char∗ napis ) {p r i n t f ( ”%s : wysyłanie sygnału przerwania do wątku\n” , napi s ) ;e r rno = pthread cance l ( id ) ;t e s t e r r n o ( ” pthread cance l ” ) ;
p r i n t f ( ”%s : wysłano , oczek iwanie na zakończen ie \n” , napi s ) ;e r rno = p t h r e a d j o i n ( id , NULL) ;t e s t e r r n o ( ” p t h r e a d j o i n ” ) ;
p r i n t f ( ”%s : wątek zakończony\n” , napi s ) ;
/∗ utworzenie wątków ∗/errno = pthr ead c r ea t e (& id [ 0 ] , NULL, watek1 , ( void ∗) (0 ) ) ;t e s t e r r n o ( ” pth r ead c r ea t e (1 ) ” ) ;
e r rno = pthr ead c r ea t e (& id [ 1 ] , NULL, watek2 , ( void ∗) (1 ) ) ;t e s t e r r n o ( ” pth r ead c r ea t e (2 ) ” ) ;
e r rno = pthr ead c r ea t e (& id [ 2 ] , NULL, watek3 , ( void ∗) (2 ) ) ;t e s t e r r n o ( ” pth r ead c r ea t e (3 ) ” ) ;
/∗ przerywanie k o l e j n y c h wątków ∗/przerwanie ( id [ 0 ] , ”#0” ) ;przerwanie ( id [ 1 ] , ”#1” ) ;przerwanie ( id [ 2 ] , ”#2” ) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : różne zachowanie mutexów w pthreads przy p r ó b i e ponownego∗ z a ł o ż e n i a b lokady∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <errno . h>
#define USE UNIX98#include <pthread . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
pthread t id ;pthread mutex t mutex ;pthread mutexatt r t mutexattr ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : zmienne warunkowe∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <pthread . h>#include <errno . h>#include <uni s td . h> // s l e e p
pthread mutex t mutex = PTHREAD MUTEX INITIALIZER;pthread cond t cond = PTHREAD COND INITIALIZER;
char warunek = 0 ;
void∗ watek ( void∗ numer ) {p r i n t f ( ”\ turuchomiono wątek #%d\n” , ( int ) numer ) ;while (1 ) {
pthread mutex lock(&mutex ) ;do {
i f ( warunek )break ;
else {p r i n t f ( ”\ twątek #%d oczeku je na sygnał . . . \ n” , ( int ) numer ) ;pthread cond wait (&cond , &mutex ) ;p r i n t f ( ”\ t . . . wątek #%d otrzymał sygnał !\n” , ( int ) numer ) ;
errno = pthread c r ea t e (& id [ i ] , NULL, watek , ( void ∗) ( i +1) ) ;i f ( errno ) {
per ro r ( ” p th r ead c r ea t e ” ) ;return EXIT FAILURE ;
}}
/∗ wysyłanie sygnałów ∗/
s l e e p (1 ) ;puts ( ” p th r ead cond s i gna l − s y g n a l i z a c j a ” ) ;p th r ead cond s i gna l (&cond ) ;
s l e e p (1 ) ;puts ( ” pthread cond broadcast − r o z g ł a s z a n i e ” ) ;pthread cond broadcast (&cond ) ;
s l e e p (1 ) ;
/∗ kończymy proces , bez og lądan ia s i ę na wątk i ∗/puts ( ” kon iec programu” ) ;return EXIT SUCCESS ;
}
76
6.2. WYKAZ PRZYKŁADÓW
Przykład 10
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : prywatne dane wątków∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>#include <uni s td . h>#include <s t r i n g . h>
#define t e s t e r r n o ( i n f o ) do { i f ( errno ) { per ro r ( i n f o ) ; e x i t (EXIT FAILURE);}} while (0 )
pthread key t k lucz ;
/∗ f u n k c j a wypsuje wiersz , p o p r z e d z ą j ą c go pre f i k sem przypisanym do wątku∗/
void wyswiet l ( const char∗ napis ) {char∗ p r e f i k s = (char∗) p t h r e a d g e t s p e c i f i c ( k lucz ) ;i f ( p r e f i k s == NULL)
/∗ n a l e ż y z a b e z p i e c z y ć s i ę przed sy tuac ją , gdy wywołującywątek nie przyporządkował nic do k l u c z a ∗/
puts ( napi s ) ;else
p r i n t f ( ”%s : %s \n” , p r e f i k s , nap i s ) ;}//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
/∗ d e s t r u k t o r k l u c z a ∗/void des t rukto r ( void∗ napis ) {
p r i n t f ( ”wywołano dest ruktor , adres pamięci do zwo ln i en ia : %p ( ’% s ’ ) \n” ,napis ,(char∗) napi s
void∗ watek ( void∗ napis ) {/∗ u s t a w i e n i e p r e f i k s u w l o k a l n y c h danych wątku ∗/int s t a t u s = p t h r e a d s e t s p e c i f i c ( klucz , napi s ) ;
77
6.2. WYKAZ PRZYKŁADÓW
i f ( s t a t u s )f p r i n t f ( s tde r r , ” p t h r e a d s e t s p e c i f i c : %s \n” , s t r e r r o r ( s t a t u s ) ) ;
elsep r i n t f ( ” adres napisu : %p ( ’% s ’ ) \n” , napis , (char∗) napi s ) ;
wyswiet l ( ”Witaj w równoległym ś w i e c i e ! ” ) ;s l e e p (1 ) ;wyswiet l ( ”Wątek wykonuje pracę ” ) ;s l e e p (1 ) ;wyswiet l ( ”Wątek zakończony ” ) ;return NULL;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : b a r i e r y∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L
#include <pthread . h>#include <s t d i o . h>#include <s t d l i b . h>#include <s t r i n g . h>#include <errno . h>#include <uni s td . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
p t h r e a d b a r r i e r t b a r i e r a ;
void∗ watek ( void∗ numer ) {int s , s t a t u s ;
s = rand ( ) % 4 + 1 ; // oczek iwanie 1−4 sp r i n t f ( ”\ twątek #%d rozpoczęty , z o s t a n i e wstrzymany na %d sekund\n” , ( int
)numer , s ) ;
s l e e p ( s ) ;
p r i n t f ( ”\ twątek #%d os i ągną ł b a r i e r ę \n” , ( int ) numer ) ;s t a t u s = p t h r e a d b a r r i e r w a i t (& b a r i e r a ) ;switch ( s t a t u s ) {
case 0 : // okbreak ;
case PTHREAD BARRIER SERIAL THREAD:p r i n t f (
”\ twszy s tk i e wątki o s i ągnę ły b a r i e r ę ”” (PTHREAD BARRIER SERIAL THREAD w wąteku #%d) \n” ,( int ) numer
) ;break ;
default :f p r i n t f ( s tde r r , ” p t h r e a d b a r r i e r w a i t : %s \n” , s t r e r r o r ( s t a t u s ) ) ;break ;
p r i n t f ( ” z o s t a n i e uruchomionych %d wątków\n” , N) ;
/∗ i n i c j a l i z a c j a b a r i e r y − N wątków ∗/errno = p t h r e a d b a r r i e r i n i t (&bar i e ra , NULL, N) ;t e s t e r r n o ( ” p t h r e a d b a r r i e r i n i t ” ) ;
/∗ utworzenie N wątków ∗/for ( i =0; i < N; i++) {
errno = pthread c r ea t e (& id [ i ] , NULL, watek , ( void ∗) i ) ;t e s t e r r n o ( ” pth r ead c r ea t e ” ) ;
}
/∗ o c z e k i w a i e na d o j ś c i e do b a r i e r y w s z y s t k i c h wątków ∗/for ( i =0; i < N; i++) {
errno = p t h r e a d j o i n ( id [ i ] , NULL) ;t e s t e r r n o ( ” p t h r e a d j o i n ” ) ;
}
/∗ z w o l n i e n i e b a r i e r y ∗/errno = p t h r e a d b a r r i e r d e s t r o y (& b a r i e r a ) ;t e s t e r r n o ( ” p t h r e a d b a r r i e r d e s t r o y ” ) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : zmiana rozmiaru s t o s u wątku∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L
#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include < l i m i t s . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
#define N (100∗1024)
/∗ wątek używa s p o r e j t a b l i c y a lokowanej na s t o s i e ∗/void∗ watek ( void∗ arg ) {
char t a b l i c a [N ] ;int i ;for ( i =0; i < N; i++)
int main ( int argc , char∗ argv [ ] ) {pthread t id ;p t h r e a d a t t r t a t t r ;s i z e t rozmiar ;
e r rno = p t h r e a d a t t r i n i t (& a t t r ) ;i f ( er rno ) {
per ro r ( ” p t h r e a d a t t r i n i t ” ) ;return EXIT FAILURE ;
}
i f ( argc > 1) {rozmiar = a t o i ( argv [ 1 ] ) ;p r i n t f ( ” rozmiar s to su usta lony przez użytkownika : %u\n” , rozmiar ) ;p r i n t f ( ”minimalny rozmiar s to su : %u\n” , PTHREAD STACK MIN) ;
er rno = p t h r e a d a t t r s e t s t a c k s i z e (&attr , rozmiar ) ;t e s t e r r n o ( ” p t h r e a d a t t r s e t s t a c k s i z e ” ) ;
82
6.2. WYKAZ PRZYKŁADÓW
}else {
p t h r e a d a t t r g e t s t a c k s i z e (&attr , &rozmiar ) ;p r i n t f ( ”domyślny rozmiar s to su : %u\n” , rozmiar ) ;
}
errno = pthr ead c r ea t e (&id , &attr , watek , NULL) ;t e s t e r r n o ( ” pth r ead c r ea t e ” ) ;
p t h r e a d j o i n ( id , NULL) ;puts ( ”wątek zakończony ” ) ;
p t h r e a d a t t r d e s t r o y (& a t t r ) ;}//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
83
6.2. WYKAZ PRZYKŁADÓW
Przykład 13
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : wysy łanie sygnałow UNIX−owych do wątków∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L#include <s t d l i b . h>#include <s t d i o . h>#include <errno . h>#include <pthread . h>#include <s i g n a l . h>#include <uni s td . h> // s l e e p
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
pthread t main id ; // id głównego wątek
// f u n k c j a wątkuvoid∗ watek ( void∗ nieuzywany ) {
puts ( ”\ twątek s i ę rozpoczą ł ” ) ;s l e e p (1 ) ;
puts ( ”\ twątek wysyła sygnał SIGUSR1 do głównego wątku” ) ;e r rno = p t h r e a d k i l l ( main id , SIGUSR1) ;t e s t e r r n o ( ” p t h r e a d k i l l ” ) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : odc zy t czasu CPU, j a k i z u ż y ł wątek∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L
#include <s t d l i b . h>#include <s t d i o . h>#include <errno . h>
#include <pthread . h>#include <uni s td . h>#include <time . h> // s l e e p#include <s t r i n g . h> // s t r e r r o r
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
// f u n k c j a zwraca czas w mi l i sekundach d l a wskazanego zegaralong c lock ms ( const c l o c k i d t i d z e g a r a ) ;
// f u n k c j a zwraca czas CPU d l a wątku (w mi l i sekundach )long ge t th r ead t ime ( pthread t id ) ;
/∗ parametry wątku ∗/typedef struct {
int id ; // numerint n ; // l i c z b a i t e r a c j i
} parametry ;
// f u n k c j a wątkuvoid∗ watek ( void∗ arg ) {
parametry∗ arg = ( parametry ∗) a rg ;int i ;p r i n t f ( ”wątek #%d uruchomiony , dwa razy wykona %d pustych p ę t l i \n” ,
( int ) arg−>id ,( int ) arg−>n
) ;
for ( i =0; i < arg−>n ; i++)/∗ z u ż y c i e czasu procesora ∗/ ;
s l e e p (2 ) ;
for ( i =0; i < arg−>n ; i++)/∗ z u ż y c i e czasu procesora ∗/ ;
86
6.2. WYKAZ PRZYKŁADÓW
/∗ podsumowanie pracy ∗/p r i n t f ( ”wątek #%d zakończony , zużył %ldms czasu proce sora \n” ,
( int ) arg−>id ,c lock ms (CLOCK THREAD CPUTIME ID)
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : s e k c j a kr y tyc zna z użyciem mutexów∗ j e ś l i zde f in iowane z o s t a n i e BLOKADA, mutex b l o k u j e∗ dos tęp do zmiennej , w przeciwnym r a z i e wątk i zmienia ją∗ j ą bez żadne j s y n c h r o n i z a c j i , co może prowadzić do błędu∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
#define N 10 /∗ l i c z b a wątków ∗/#define K 1000 /∗ l i c z b a i t e r a c j i ( z tą war toś c ią n a l e ż y eksperymentować )
∗/
pthread mutex t blokada ;int l i c z n i k = 0 ; // g l o b a l n y l i c z n i k , powinien być chroniony b lokadą
void ms s leep (unsigned ms) {struct t imespec req ;req . t v s e c = (ms / 1000) ;req . tv n s e c = (ms % 1000 ∗ 1000000) ;nanos leep(&req , NULL) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : b lokady z a p i s / odczy ( rwlock )∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define POSIX C SOURCE 200809L#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <errno . h>#include <time . h>
void ms s leep ( const unsigned ms) ;#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}
while (0 )
pthread rw lock t blokada ;int wartosc ; // o b i e k t chroniony b lokadą
/∗ wątek zmienia wartość ∗/void∗ p i s a r z ( void∗ numer ) {
while (1 ) {p r i n t f ( ” p i s a r z #%d czeka na dostęp \n” , ( int ) numer ) ;e r rno = pthread rwlock wr lock (&blokada ) ;t e s t e r r n o ( ” pthread rwlock wr lock ” ) ;p r i n t f ( ” p i s a r z #%d ustawia nową wartość \n” , ( int ) numer ) ;
ms s l eep (113) ;
p r i n t f ( ” p i s a r z #%d zwalnia blokadę \n” , ( int ) numer ) ;e r rno = pthread rwlock un lock (&blokada ) ;t e s t e r r n o ( ” pthread rwlock un lock ” ) ;
/∗ wątek t y l k o o d c z y t u j e wartość ∗/void∗ c z y t e l n i k ( void∗ numer ) {
int errno ;while (1 ) {
p r i n t f ( ” c z y t e l n i k #%d czeka na dostęp \n” , ( int ) numer ) ;
91
6.2. WYKAZ PRZYKŁADÓW
errno = pthread rw lock rd lock (&blokada ) ;t e s t e r r n o ( ” pthread rw lock rd lock ” ) ;p r i n t f ( ” c z y t e l n i k #%d odczytu je wartość \n” , ( int ) numer ) ;
ms s l eep (13) ;
p r i n t f ( ” c z y t e l n i k #%d zwalnia blokadę \n” , ( int ) numer ) ;e r rno = pthread rwlock un lock (&blokada ) ;t e s t e r r n o ( ” pthread rwlock un lock ” ) ;
void ms s leep ( const unsigned ms) {struct t imespec req ;req . t v s e c = (ms / 1000) ;req . tv n s e c = (ms % 1000 ∗ 1000000) ;nanos leep(&req , NULL) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : s y n c h r o n i z a c j a między wątkami różnych procesów∗ w s p ó ł d z i e l o n y mutex i zmienna warunkowa∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define XOPEN SOURCE#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <pthread . h>#include <sys /shm . h>
#include <errno . h>#include <uni s td . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
struct pamiec dz i e l ona {/∗ w s p ó ł d z i e l o n e mutex i zmienna warunkowa ∗/pthread mutex t mutex ;pthread cond t cond ;pthread mutexatt r t mattr ;p th r ead condat t r t c a t t r ;
/∗ dane ∗/char napi s dostepny ;char napis [ 2 5 6 ] ;
} ∗pamiec ;
/∗ proces 1 tworzy segment pamięci wspólnej , tworzy mutex i zmiennąwarunkową WSPÓŁDZIELONEz innymi procesami , po czym o c z e k u j e na sygnał , aż napis s t a n i e s i ę
dostępny ∗/void proces1 ( ) {
int shmid ;int errno ;
shmid = shmget (IPC PRIVATE, s izeof ( pamiec ) , 0666) ;t e s t e r r n o ( ”shm open” ) ;p r i n t f ( ” id segmentu pamięci d z i e l o n e j : %d\n” , shmid ) ;
pamiec = shmat ( shmid , 0 , 0) ;t e s t e r r n o ( ”shmat” ) ;p r i n t f ( ” adres przyłączonego segmentu : %p\n” , ( void ∗) pamiec ) ;
93
6.2. WYKAZ PRZYKŁADÓW
/∗ i n i c j a l i z a c j a ∗/pamiec−>napi s dostepny = 0 ;memset ( pamiec−>napis , 0 , s izeof ( pamiec−>napis ) ) ;
/∗ tw orz en ie mutexu i zmiennej warunkowej ∗/errno = pth r ead mutexa t t r i n i t (&pamiec−>mattr ) ;t e s t e r r n o ( ” p th r ead mutexa t t r i n i t ” ) ;
e r rno = pthread mutexat t r se tpshared (&pamiec−>mattr ,PTHREAD PROCESS SHARED) ;
t e s t e r r n o ( ” pthread mutexat t r se tpshared ” ) ;
e r rno = pthread mutex in i t (&pamiec−>mutex , &pamiec−>mattr ) ;t e s t e r r n o ( ” pthread mutex in i t ” ) ;
/∗ tw orz en ie mutexu i zmiennej warunkowej ∗/errno = p t h r e a d c o n d a t t r i n i t (&pamiec−>c a t t r ) ;t e s t e r r n o ( ” p t h r e a d c o n d a t t r i n i t ” ) ;
e r rno = pthread condat t r s e tp sha red (&pamiec−>cat t r ,PTHREAD PROCESS SHARED) ;
t e s t e r r n o ( ” pthread condat t r s e tp sha red ” ) ;
e r rno = p t h r e a d c o n d i n i t (&pamiec−>cond , &pamiec−>c a t t r ) ;t e s t e r r n o ( ” p t h r e a d c o n d i n i t ” ) ;
/∗ oczek iwanie na u s t a w i e n i e napisu p r z e z inny proces ∗/errno = pthread mutex lock(&pamiec−>mutex ) ;t e s t e r r n o ( ” pthread mutex lock ” ) ;do {
i f ( pamiec−>napi s dostepny ) {p r i n t f ( ” inny proces us tawi ł napi s : ’%s ’\n” , pamiec−>napis ) ;break ;
}else {
puts ( ” pthread cond wait ” ) ;e r rno = pthread cond wait (&pamiec−>cond , &pamiec−>mutex ) ;t e s t e r r n o ( ” pthread cond wait ” ) ;
}} while (1 ) ;
e r rno = pthread mutex unlock(&pamiec−>mutex ) ;t e s t e r r n o ( ” pthread mutex unlock ” ) ;
/∗ o d ł ą c z e n i e od segmentu pamięci ∗/shmdt ( pamiec ) ;t e s t e r r n o ( ”shmdt” ) ;
/∗ i skasowanie go ∗/shmctl ( shmid , IPC RMID , NULL) ;
/∗ proces 2 p r z y ł ą c z a s i ę do segmentu i używając w s p ó ł d z i e l o n e g o mutexu izmiennejwarunkowej ustawia napis i s y g n a l i z u j e go procesowi 1 f u n k c j ą
p t h r e a d c o n d s i g n a l ∗/void proces2 ( int shmid , const char∗ napis ) {
int errno ;
pamiec = shmat ( shmid , 0 , 0) ;t e s t e r r n o ( ”shmat” ) ;p r i n t f ( ” adres przyłączonego segmentu : %p\n” , ( void ∗) pamiec ) ;
e r rno = pthread mutex lock(&pamiec−>mutex ) ;t e s t e r r n o ( ” pthread mutex lock ” ) ;
s t r c a t ( pamiec−>napis , nap i s ) ; // uwaga : możliwe p r z e p e ł n i e n i e bu forapamiec−>napi s dostepny = 1 ;
errno = pthr ead cond s i gna l (&pamiec−>cond ) ;t e s t e r r n o ( ” p th r ead cond s i gna l ” ) ;
e r rno = pthread mutex unlock(&pamiec−>mutex ) ;t e s t e r r n o ( ” pthread mutex unlock ” ) ;
p r i n t f ( ” proces us tawi ł napi s ’%s ’ i wykonał p th r ead cond s i gna l \n” , napi s) ;
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : p t h r e a d a t f o r k∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define XOPEN SOURCE 700#include <s t d l i b . h>#include <s t d i o . h>#include <s t r i n g . h>#include <pthread . h>#include <errno . h>#include <uni s td . h> // s l e e p#include <sys / wait . h> // w a i t p i d
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
void∗ watek ( void∗ numer ) {p r i n t f ( ”\ turuchomiono wątek #%d\n” , ( int ) numer ) ;while (1 ) {
p r i n t f ( ”\ t \ twątek #%d w p r o c e s i e #%d\n” , ( int )numer , ge tp id ( ) ) ;u s l e ep (700∗1000) ;
puts ( ” początek programu” ) ;i n i c j a l i z a c j a w a t k o w ( ) ;
/∗ r e j e s t r o w a n i e f u n k c j i wykonywanej w p r o c e s i e potomnym ∗/errno = pthr ead a t f o rk (NULL, NULL, i n i c j a l i z a c j a w a t k o w ) ;t e s t e r r n o ( ” pth r ead a t f o rk ” ) ;
s l e e p (1 ) ;
pid = fo rk ( ) ;p r i n t f ( ” f o rk => %d\n” , pid ) ;switch ( pid ) {
case −1:t e s t e r r n o ( ” f o rk ” ) ;break ;
case 0 : // proces potomnys l e e p (2 ) ;break ;
default : // proces nadrzędnywaitp id ( pid , NULL, 0) ;t e s t e r r n o ( ” waitp id ” ) ;break ;
}
/∗ kończymy proces , bez og lądan ia s i ę na wątk i ∗/return EXIT SUCCESS ;
}
97
6.2. WYKAZ PRZYKŁADÓW
Przykład 19
/∗∗ Przykładowy program d l a kursu ”POSIX Threads” z w i k i b o o k s . p l∗∗ Temat : p r i o r y t e t y wątków∗∗ Autor : Wojciech Muła∗ Ostatn ia zmiana : 2010−03−xx∗/
#define XOPEN SOURCE 500#include <s t d l i b . h>#include <s t d i o . h>#include <pthread . h>#include <sched . h>#include <errno . h>#include <uni s td . h>
#define t e s t e r r n o (msg) do{ i f ( errno ) { per ro r (msg) ; e x i t (EXIT FAILURE) ;}}while (0 )
typedef struct {int l i c z n i k ;char p r z e r w i j ;int p r i o r y t e t ;
} Arg ;
/∗ f u n k c j a wykonywana w wątku − z w i ę k s z a l i c z n i k ∗/void∗ watek ( void∗ arg ) {
Arg ∗ arg = ( Arg∗) a rg ;
arg−> l i c z n i k = 0 ;while ( ! arg−>p r z e r w i j ) {
int main ( int argc , char∗ argv [ ] ) {pthread t id [N ] ;p t h r e a d a t t r t a t t r ;Arg arg [N ] ;int pmin , pmax ;int i , s c h e d p o l i c y ;struct sched param sp ;
s c h e d p o l i c y = SCHED OTHER;
98
6.2. WYKAZ PRZYKŁADÓW
i f ( argc > 1)switch ( a t o i ( argv [ 1 ] ) ) {
case 0 :s c h e d p o l i c y = SCHED OTHER;break ;
case 1 :s c h e d p o l i c y = SCHED RR;break ;
case 2 :s c h e d p o l i c y = SCHED FIFO;break ;
pmin = s c h e d g e t p r i o r i t y m i n ( s c h e d p o l i c y ) ;pmax = s c h e d g e t p r i o r i t y m a x ( s c h e d p o l i c y ) ;switch ( s c h e d p o l i c y ) {
case SCHED OTHER:p r i n t f ( ”SCHED OTHER: p r i o r y t e t y w z a k r e s i e %d . . . %d\n” , pmin , pmax) ;break ;
case SCHED RR:p r i n t f ( ”SCHED RR: p r i o r y t e t y w z a k r e s i e %d . . . %d\n” , pmin , pmax) ;break ;
case SCHED FIFO:p r i n t f ( ”SCHED FIFO: p r i o r y t e t y w z a k r e s i e %d . . . %d\n” , pmin , pmax) ;break ;
}
errno = p t h r e a d a t t r i n i t (& a t t r ) ;t e s t e r r n o ( ” p t h r e a d a t t r i n i t ” ) ;
/∗ parametry szeregowania odczytywane z atrybutów ∗/errno = p t h r e a d a t t r s e t i n h e r i t s c h e d (&attr , PTHREAD EXPLICIT SCHED) ;t e s t e r r n o ( ” p t h r e a d a t t r s e t i n h e r i t s c h e d ” ) ;
/∗ wybór podanego algorytmu szeregowania ∗/errno = p t h r e a d a t t r s e t s c h e d p o l i c y (&attr , s c h e d p o l i c y ) ;t e s t e r r n o ( ” p t h r e a d a t t r s e t s c h e d p o l i c y ” ) ;
/∗ utworzenie k i l k u wątków wątku z różnymi p r i o r y t e t a m i ∗/for ( i =0; i < N; i++) {
/∗ k o l e j n e wątk i mają coraz wyższe p r i o r y t e t y ∗/sp . s c h e d p r i o r i t y = pmin + (pmax−pmin ) ∗ i /( f loat ) (N−1) ;arg [ i ] . p r z e r w i j = 0 ;arg [ i ] . l i c z n i k = 0 ;arg [ i ] . p r i o r y t e t = sp . s c h e d p r i o r i t y ;
/∗ u s t a w i e n i e p r i o r y t e t u ∗/errno = pthread at t r s e t schedparam(&attr , &sp ) ;t e s t e r r n o ( ” pthread at t r s e t schedparam ” ) ;
99
6.2. WYKAZ PRZYKŁADÓW
/∗ uruchomienie wątku ∗/errno = pthread c r ea t e (& id [ i ] , &att r , watek , &arg [ i ] ) ;t e s t e r r n o ( ” pth r ead c r ea t e ” ) ;
p r i n t f ( ”utworzono wątek #%d o p r i o r y t e c i e %d\n” , i , arg [ i ] . p r i o r y t e t ) ;}
errno = p t h r e a d a t t r d e s t r o y (& a t t r ) ;t e s t e r r n o ( ” p t h r e a d a t t r d e s t r o y ” ) ;
/∗ oczek iwanie ∗/s l e e p (2 ) ;
/∗ u s t a w i e n i e f l a g i zakończenia pracy , k tór ą t e s t u j ą f u n k c j e wątkóworaz od czy t i c h b i e ż ą c y c h l i c z n i k ó w ∗/
for ( i =0; i < N; i++) {arg [ i ] . p r z e r w i j = 1 ;p r i n t f ( ”wątek #%d ( p r i o r y t e t %3d) : l i c z n i k = %d\n” ,
i ,arg [ i ] . p r i o r y t e t ,arg [ i ] . l i c z n i k
) ;}
/∗ t e r a z oczek iwanie na i c h zakończen ie ∗/for ( i =0; i < N; i++) {
errno = p t h r e a d j o i n ( id [ i ] , NULL) ;t e s t e r r n o ( ” p t h r e a d j o i n ” ) ;