Oprogramowanie systemów równoległych i rozproszonych Wykład 2

Transkrypt

Oprogramowanie systemów równoległych i rozproszonych Wykład 2
Biblioteka Pthreads
Zestaw funkcji dotyczacy
˛ watków
˛
zdefiniowany został przez norme˛
POSIX P1003.4a i nosi nazw˛e Pthreads (skrót od POSIX threads).
Oprogramowanie systemów
równoległych i rozproszonych
Jest to zbiór typów i funkcji jezyka
˛
C.
Implementacja pakietu istnieje miedzy
˛
innymi w systemach Linux,
QNX6, DEC OSF1.
Obecnie watki
˛ sa˛ cz˛eścia˛ biblioteki glibc (od wersji 2).
Wykład 2
Dr inż. Tomasz Olas
[email protected]
Instytut Informatyki Teoretycznej i Stosowanej
Politechnika Cz˛estochowska
Wykład 2
Wykład 2 – p. 1/48
Zasoby watku
˛
Watki
˛ POSIX - grupy funkcji
Watek
˛
korzysta z zasobów procesu, ale może być szeregowany do
wykonania jako niezależna jednostka w ramach procesu.
Funkcje realizujace
˛ watki
˛ POSIX można podzielić na trzy grupy:
Zarzadzanie
˛
watkami
˛
- funkcje do tworzenia, zarzadzania,
˛
usuwania watków,
˛
oraz funkcje zwiazane
˛
z atrybutami watków.
˛
Ma swój własny przebieg i własne zasoby lokalne:
stos,
Obiekty mutex - funkcje realizujace
˛ synchronizacje dostepu
˛
do
rejestry,
zasobów („MUTual EXclusion” - wzajemne wykluczanie).
Funkcje zapewniaja˛ tworzenie, usuwanie, otwieranie i
zamykanie obiektów mutex.
sposób kolejkowania (szeregowania): np. priorytet,
zbiór sygnałów,
Zmienne warunkowe - funkcje realizujace
˛ komunikacje˛ miedzy
˛
lokalne dane watku.
˛
Pozostałe zasoby watki
˛ dziela˛ ze soba˛ w ramach procesu - pamieć,
˛
instrukcje programu, ID procesu, deskryptory plików, dzielone
biblioteki, mechanizmy komunikacji miedzyprocesorowej,
˛
itd.
Wykład 2 – p. 3/48
watkami
˛
dzielacymi
˛
obiekty mutex. Wykorzystuja˛ warunki
zdefiniowane przez programiste.
˛ Sa˛ to funkcje do tworzenia,
usuwania, czekania i wysyłania sygnału przy określonej
wartości zmiennej.
Wykład 2
Tworzenie watku
˛
(I)
Konwencja nazw
Nowy watek tworzy sie przy pomocy funkcji pthread_create.
Przedrostek funkcji
Grupa funkcji
pthread
Funkcje watków
˛
oraz funkcje pomocnicze
pthread_attr
Atrybuty watków
˛
pthread_mutex
Obiekty mutex
pthread_mutexattr
Atrybuty obiektów mutex
pthread_cond
Zmienne warunkowe
pthread_condattr
Atrybuty zmiennych warunkowych
pthread_key
Specyficzne dla watku
˛
klucze (dane lokalne)
Funkcja ta tworzy watek, którego kod znajduje sie w funkcji podanej
jako argument func. Watek jest uruchamiany z parametrem arg, a
informacja o nim jest umieszczana w strukturze thread.
int pthread_create(pthread_t *thread, pthread_attr_t *attr,
void (* func)(void *), void *arg)
thread - identyfikator watku
˛
(wartość nadawana przez funkcje),
attr - atrybuty watku,
˛
gdy NULL przyjete
˛ domyślne,
func - procedura zawierajaca
˛ kod watku,
˛
arg - argument przekazywany do watku,
˛
funkcja zwraca:
0 - sukces,
-1 - bład.
˛
Wykład 2 – p. 5/48
Tworzenie watku
˛
(II)
Wykład 2
Zakończenie działania watku
˛
Funkcja wykonywana przez watek
˛
powinna mieć postać:
Watek
˛
może być zakończony w nastepuj
˛ acy
˛ sposób:
Nastepuje
˛
powrót z procedury startujacej
˛ (głównej procedury
watku),
˛
void *f(void *arg);
Przykład:
Watek
˛
wywołuje funkcje pthread_exit().
void *run(void *arg)
{
...
}
Watek
˛
jest usuwany przez inny watek
˛
za pomoca˛ funkcji
pthread_cancel(),
Cały proces zostaje zakończony przez wywołanie funkcji exit
lub exec().
int main(int argc, char** argv)
{
...
pthread_t threadId;
if (pthread_create(&threadId, NULL, run, NULL))
{
std::cerr << "bład
˛ podczas tworzenia watku"
˛
<< std::endl;
}
}
Jawne zakończenie działania bieżacego
˛
watku
˛
nastepuje
˛
poprzez
wywołanie funkcji:
int pthread_exit(void ** retval)
retval - wartość zwracana przez watek
˛
(przekazywana do watków
˛
czekajacych
˛
pthread_join).
Wykład 2 – p. 7/48
Wykład 2
Kończenie watku
˛
- zasoby
Oczekiwanie na zakończenie watku
˛
Możliwe sa˛ dwa sposoby postepowania
˛
z zasobami zakończonych
watków:
˛
Z chwila˛ zakończenia watku
˛
zwalniane sa˛ wszystkie jego
zasoby.
Watek
˛
może oczekiwać na zakończenie działania innego watku
˛
przez wywołanie funkcji pthread_join.
int pthread_join( pthread_t *thread_id, void **thread_return)
thread_id - identyfikator watku
˛
na zakończenie którego bedzie
˛
czekał wołajacy
˛ watek,
˛
Zasoby zwalniane sa˛ z chwila˛ dołaczenia
˛
bieżacego
˛
watku
˛
do
innego watku
˛
(który wywołał funkcje˛ pthread_join).
Sposób postepowania
˛
z watkiem
˛
jest uzależniony od atrybutu
ustawionego w momencie tworzenia watku.
˛
PTHREAD_CREATE_DETACHED - zasoby watku
˛
zwalniane sa˛ tuż
po jego zakończeniu,
PTHREAD_CREATE_JOINABLE - zasoby watku
˛
zwalniane sa˛ po
wywołaniu funkcji pthread_join(...).
thread_return - jeśli jest różny od NULL, to wówczas kod zakończenia watku
˛
thid
zostanie wstawiony pod adres wskazywany przez thread_return. Kodem zakończenia
może być też wartość określona przy wołaniu funkcji pthread_exit lub
PTHREAD_CANCELLED jeśli watek
˛
został usuniety.
˛
Watek
˛
do którego jest dołaczany
˛
dany watek
˛
musi być w stanie
umożliwiajacym
˛
dołaczenie.
˛
Funkcja pthread_join powinna być wykonana dla każdego nie
odłaczonego
˛
watku.
˛
Wykład 2
Wykład 2 – p. 9/48
Odłaczenie
˛
działajacego
˛
watku
˛
Zasoby watków
˛
- przykład
W1 i W2
ustawiony atrybut
PTHREAD_CREATE_JOINABLE
Funkcja pthread_detach odłacza
˛
podany watek,
˛
co gwarantuje, że
zasoby pamieci
˛ zostana˛ zwolnione natychmiast po zakończeniu
działania watku.
˛
W3 i W4
ustawiony atrybut
PTHREAD_CREATE_DETACHED
int pthread_detach( pthread_t *thread_id);
pthread_create(...)
thread_id - identyfikator watku,
˛
który zostanie odłaczony.
˛
Zakonczenie
pthread_exit(...)
Zakonczenie i
zwolnienie
zasobów
pthread_join(...)
Zwolnienie
zasobów
Wykład 2 – p. 11/48
Wykład 2
Tworzenie watków
˛
- przykład (I)
Tworzenie watków
˛
- przykład (II)
#include <pthread.h>
#include <unistd.h>
#include <iostream>
int main()
{
pthread_t thread;
int id1 = 1;
if (pthread_create(&thread, NULL, NewThread, (void *)(&id1)))
{
std::err << "bład
˛ podczas tworzenia watku
˛
nr 1" << std::endl;
exit(1);
}
pthread_detach(thread);
void* NewThread(void* arg)
{
int id = *static_cast<int*>(arg);
for (int i = 0; i < 3; i++)
{
std::cout << id << " " << std::flush;
sleep(1);
}
return NULL;
}
int id2 = 2;
if (pthread_create(&thread, NULL, NewThread, (void *)(&id2)))
{
std::cerr << "bład
˛ podczas tworzenia watku
˛
nr 2" << std::endl;
exit(1);
}
pthread_detach(thread);
pthread_exit(NULL);
}
Wykład 2 – p. 13/48
Wykład 2
Funkcja pthread_join - przykład (I)
Tworzenie watków
˛
- przykład (III)
#include
#include
#include
#include
Kompilacja:
g++ -o watki watki.cpp -lpthread
lub
<pthread.h>
<iostream>
<unistd.h>
<fstream>
g++ -pthread -o watki watki.cpp
const int size = ...;
Wynik działania programu:
void * SaveThread(void * arg)
{
std::cout << "Begin Thread" << std::endl;
ofstream os("ala.txt");
for (int i = 0; i < size; i++)
os << "0";
std::cout << "End Thread" << std::endl;
1 2 1 2 1 2
return NULL;
}
Wykład 2 – p. 15/48
Wykład 2
Funkcja pthread_join - przykład (II)
Funkcja pthread_join - przykład (III)
int main()
{
std::cout << "Begin Program" << std::endl;
pthread_t thread;
if (pthread_create(&thread, NULL, SaveThread, NULL))
{
cerr << "bład
˛ podczas tworzenia watku"
˛
<< std::endl;
exit(1);
}
for (int i = 0; i < 30; i++)
{
sleep(1);
std::cout << i << " " << flush;
}
void* result;
pthread_join(thread, &result);
std::cout << std::endl << "End program \n" << std::endl;
Wynik działania programu dla size = 50000000:
Begin
Begin
0 1 2
21 22
Program
Thread
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
23 24 25 26 27 28 29 End Thread
End program
Wynik działania programu dla size = 5000000:
Begin Program
Begin Thread
0 1 2 3 4 5 End Thread
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29
End program
pthread_exit(NULL);
}
Wykład 2 – p. 17/48
Obiekty mutex
Wykład 2
Tworzenie obiektu mutex
Mutex jest mechanizmem wzajemnego wykluczania („MUTual
EXclusion”) służacych
˛
do ochrony danych wspólnych dla watków
˛
przed jednoczesnymi modyfikacjami.
Mechanizm ten może służyć do implementacji sekcji krytycznych,
semaforów i monitorów.
Obiekt mutex jest zmienna˛ typu pthread_mutex_t.
Może zostać zainicjowany poprzez:
Stała˛ PTHREAD_MUTEX_INITIALIZER:
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
Funkcje˛ pthread_mutex_init:
int pthread_mutex_init(pthread_mutex_t * mutex,
const pthread_mutexattr_t * mutexattr);
Mutex ma dwa stany:
otwarty - nie zajety
˛ przez żaden watek,
˛
zaj˛ety - zajety
˛ przez jeden watek.
˛
mutex - wskaźnik do inicjowanego obiektu mutex,
mutexattr -atrybuty tworzonego obiektu mutex (jeśli NULL ustawiane sa˛
atrybuty domyślne).
Mutex nie może być jednocześnie zajety
˛ przez wiecej
˛
niż jeden
watek.
˛
Watek
˛
próbujacy
˛ zajać
˛ już zajety
˛ watek
˛
zostaje wstrzymany do chwili
zwolnienia mutexu przez watek,
˛
który go zajał˛ wcześniej.
Wykład 2 – p. 19/48
Wykład 2
Zajecie
˛
i zwolnienie obiektu mutex
Funkcja pthread_mutex_lock
Na obiekcie mutex wykonuje sie˛ dwie podstawowe operacje: zajecie
˛
i zwolnienie obiektu mutex.
Przed dostepem
˛
do współdzielonego zasobu watek
˛
musi zapewnić
sobie wyłaczność
˛
w korzystaniu z tego zasobu.
Do zajecia
˛
obiektu mutex służy funkcja pthread_mutex_lock,
natomiast do zwolnienia funkcja pthread_mutex_unlock.
W tym celu może wywołać funkcje:
Thread 1
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex - zadeklarowana wcześniej i zainicjowana zmienna typu
pthread_mutex_t.
Thread 2
Jeśli mutex jest wolny, to zostaje zajety
˛ oraz przypisany watkowi
˛
wołajacemu.
˛
Funkcja natychmiast kończy działanie.
Jeśli mutex jest zajety
˛ przez inny watek,
˛
to zawiesza działanie watku
˛
do momentu, kiedy mutex zostanie zwolniony.
mutex_lock(mutex)
blokada
mutex_lock(mutex)
uzycie zasobu
odblokowanie
mutex_unlock(mutex)
uzycie zasobu
mutex_unlock(mutex)
Wykład 2 – p. 21/48
Wykład 2
Funkcja pthread_mutex_unlock
Obiekty mutex - przykład
Funkcja pthread_mutex_unlock zwalnia zajety
˛ mutex.
// utworzenie i zainicjowanie muteksu
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex - zadeklarowana wcześniej i zainicjowana zmienna typu
pthread_mutex_t.
void * run(void * arg)
{
...
Mutex musi być wcześniej zajety
˛ przez wołajacy
˛ watek.
˛
pthread_mutex_lock(&mutex);
// zaj˛
ecie muteksu - protokół wst˛
epny
// operacje na zasobie dzielonym - sekcja krytyczna
...
pthread_mutex_unlock(&mutex); // zwolnienie muteksu- protokół końcowy
...
}
Wykład 2 – p. 23/48
Wykład 2
Obiekty mutex - pozostałe funkcje
Operacje na obiektach mutex - przykład (I)
Funkcja pthread_mutex_trylock zachowuje sie˛ podobnie jak
funkcja pthread_mutex_lock. Nie blokuje ona jednak wołajacego
˛
watku,
˛
ale w przypadku zajetego
˛
obiektu mutex zwraca EBUSY.
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#include <pthread.h>
#include <iostream>
#include <unistd.h>
const int size = 100;
int sum = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Do kasowania obiektu mutex służy funkcja
pthread_mutex_destroy. Niszczy obiekt mutex oraz zwalnia
zwiazane
˛
z nim zasoby. Mutex musi być wolny, w przeciwnym
wypadku zwracana jest wartość EBUSY.
void * ForThread(void * arg)
{
int* tab = static_cast< int* >(arg);
for (int i = 0; i < size/2; i++)
{
pthread_mutex_lock(&mutex);
sum += tab[i];
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int pthread_mutex_destroy(pthread_mutex_t * mutex);
Wykład 2 – p. 25/48
Operacje na obiektach mutex - przykład (II)
int main()
{
pthread_t thread1, thread2;
int tab[size];
for (int j = 0; j < size; j++)
tab[j] = j;
if (pthread_create(&thread1, NULL, ForThread,
{
std::cerr << "bład
˛ podczas tworzenia watku"
˛
exit(1);
}
if (pthread_create(&thread2, NULL, ForThread,
{
std::cerr << "bład
˛ podczas tworzenia watku"
˛
exit(1);
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
std::cout << "Suma: " << sum << std::endl;
return 0;
}
Wykład 2
Zmienne warunkowe
Zmienne warunkowe sa˛ mechanizmem umożliwiajacym
˛
zawieszenie
i zwolnienie czasu procesora (watku)
˛
do momentu, w którym
zostanie spełniony określony warunek.
Warunek ten może być dowolny i niezależny od zmiennej
warunkowej, np. osiagni
˛ ecie
˛
przez zmienna˛ określonej wartości.
tab))
Zmienna warunkowa musi być zawsze otoczona obiektem mutex,
aby uniknać
˛ jednoczesnej próby oczekiwania i sygnalizowania na
zmiennej warunkowej.
<< std::endl;
tab+size/2))
<< std::endl;
Wykład 2 – p. 27/48
Wykład 2
Oczekiwanie na sygnał
Tworzenie zmiennych warunkowych
Zmienna warunkowa, to zmienna typu pthread_cond_t.
Funkcja pthread_cond_wait służy do zawieszenia watku
˛
w
oczekiwaniu na sygnał:
Przed użyciem zmienna warunkowa musi zostać zainicjowana:
Statycznie przy pomocy stałej PTHREAD_COND_INITIALIZER:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_wait(pthread_cond_t * cond,
pthread_mutex_t * mutex);
cond - zadeklarowana i zainicjowana zmienna warunkowa,
mutex - wskaźnik do obiektu mutex, którym sa˛ otoczone operacje na
Dynamicznie przy pomocy funkcji:
int pthread_cond_init(pthread_cond_t *cond,
pthread_condattr_t *cond_attr);
zmiennych warunkowych.
Funkcja pthread_cond_wait w sposób atomowy zwalnia mutex
(tak jak funkcja pthread_mutex_unlock) i oczekuje na sygnał o
spełnienie zmiennej warunkowej cond. Wykonanie watku
˛
jest
zawieszone i nie zajmuje on czasu procesora aż do momentu
odebrania sygnału od zmiennej warunkowej. Mutex musi być zajety
˛
przez watek
˛
wołajacy
˛ pthread_cond_wait. Przed końcem
działania pthread_cond_wait zajmuje mutex.
cond - wskaźnik do inicjowanej zmiennej warunkowej typu
pthread_cond_t,
cond_attr - atrybuty zmiennej warunkowej. Gdy NULL przyjete
˛ bed
˛ a˛
atrybuty domyślne.
Wykład 2
Wykład 2 – p. 29/48
Sygnalizacja spełnienia warunku
Zmienne warunkowe - przykład użycia
Funkcja pthread_cond_signal wznawia jeden z watków
˛
oczekujacych
˛
na sygnał:
Watek
˛
1 (oczekujacy
˛ na warunek):
pthread_mutex_lock(&m);
pthread_cond_wait(&cond, &m);
pthread_mutex_unlock(&m);
int pthread_cond_signal(pthread_cond_t * cond);
cond - zadeklarowana i zainicjowana zmienna warunkowa.
Jeśli żaden z watków
˛
nie oczekuje na sygnał, to nic sie˛ nie dzieje.
Watek
˛
2 (sygnalizujacy
˛ spełnienie warunku):
pthread_mutex_lock(&m);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&m);
Jeśli w oczekiwaniu na sygnał znajduje sie˛ wiecej
˛
watków,
˛
to
wznawiany jest jeden z nich.
Do wznowienia wszystkich oczekujacych
˛
na sygnał watków
˛
służy
funkcja:
Watek 1
Watek 2
mutex_lock(m)
int pthread_cond_broadcast(pthread_cond_t * cond);
blokada
mutex_lock(m)
blokada
cond_signal(c)
odblokowanie
ustawienie
warunku
cond_wait(c,m)
odblokowanie
uzycie zasobu
mutex_unlock(m)
odblokowanie
mutex_unlock(m)
Wykład 2 – p. 31/48
Wykład 2
Czasowe oczekiwanie na sygnał
Skasowanie zmiennej warunkowej
Do czasowego zawieszenia watku
˛
w oczekiwaniu na sygnał służy
funkcja:
int pthread_cond_timedwait(pthread_cond_t * cond,
pthread_mutex_t * mutex,
const struct timespec * abstime);
Do skasowania zmiennej warunkowej i zwolnienia jej zasobów służy
funkcja:
int pthread_cond_destroy( pthread_cond_t * cond);
cond - zadeklarowana i zainicjowana zmienna warunkowa.
Funkcja zwraca kod EBUSY jeśli nastapiła
˛
próba usuniecia
˛
zmiennej
warunkowej, na której oczekiwały watki.
˛
cond - zadeklarowana i zainicjowana zmienna warunkowa,
mutex - mutex, którym jest otoczona zmienna warunkowa,
czas jaki watek
˛
bedzie
˛
oczekiwał na sygnał.
Funkcja zwraca ETIMEDOUT jeśli sygnał nie wystapił
˛ w podanym
czasie (abstime).
Wykład 2
Wykład 2 – p. 33/48
Zmienne warunkowe - przykład (I)
Zmienne warunkowe - przykład (II)
int number;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* OutputThread(void* arg)
{
while (true)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout << "Wygenerowana liczba - " << number
<< " jest mniejsza od 5" << std::endl;
pthread_mutex_unlock(&mutex);
}
}
void* RandomThread(void* arg)
{
srandom(1);
for (int i=0; i<20; i++)
{
pthread_mutex_lock(&mutex);
number = static_cast<double>(rand())/RAND_MAX*10;
if (number < 5)
{
std::cout << "mniejsza";
pthread_cond_broadcast(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
Wykład 2 – p. 35/48
Wykład 2
Odwołanie watku
˛
Zmienne warunkowe - przykład (III)
int main()
{
pthread_t thread1;
if (pthread_create(&thread1,
{
std::cerr << "bład
˛ podczas
exit(1);
}
pthread_t thread2;
if (pthread_create(&thread2,
{
std::cerr << "bład
˛ podczas
exit(1);
}
Odwołanie jest mechanizmem, który umożliwia zakończenie
działania innego watku
˛
przez dany watek.
˛
Zależnie od ustawień watek,
˛
do którego wysłano takie żadanie
˛
może
je zignorować, uwzglednić
˛
to żadanie
˛
(zakończyć swoje działanie)
natychmiast lub odwlec zakończenie aż do osiagni
˛ ecia
˛
pierwszego
punktu zwanego punktem odwołania.
NULL, RandomThread, NULL))
tworzenia watku"
˛
<< std::endl;
Do wysłania żadania
˛
odwołania watku
˛
służy funkcja:
NULL, OutputThread, NULL))
int pthread_cancel(pthread_t thread);
thread - identyfikator watku
˛
do którego wysyłane jest żadanie
˛
odwołania.
tworzenia watku"
˛
<< std::endl;
Watek
˛
może odwołać samego siebie.
void* result;
pthread_join(thread1, &result);
pthread_cancel(thread2);
pthread_exit(NULL);
}
Wykład 2 – p. 37/48
Wykład 2
Punkty odwołania
Sposób reakcji na żadanie
˛
odwołania
Do zmiany sposobu reakcji na żadanie
˛
odwołania watku
˛
służa˛
funkcje:
Punkty odwołania sa˛ miejscami w programie, gdzie wykonywany jest
test, czy nie przyszło żadanie
˛
odwołania watku
˛
i w przypadku
pozytywnego wyniku testu wykonywane jest odwołanie.
int pthread_setcancelstate(int state , int * old_state );
Standard POSIX ustala nastepuj
˛ ace
˛ funkcje jako punkty odwołania:
state - nowy typ reakcji:
PTHREAD_CANCEL_ENABLE - umożliwia odwołanie,
PTHREAD_CANCEL_DISBLE - powoduje ignorowanie przychodzacych
˛
pthread_join()
pthread_cond_wait()
żada
˛ ń odwołania,
pthread_cond_timedwait()
old_state - poprzez ten argument funkcja może zwrócić poprzedni typ
reakcji (o ile nie został przekazany NULL).
pthread_testcancel()
sem_wait()
int pthread_setcanceltype(int type, int * old_type);
sigwait()
type - nowy typ reakcji:
PTHREAD_CANCEL_ASYNCHRONOUS - powodujacy
˛ natychmiastowe
Funkcja pthread_testcancel() jest używana w dużych
fragmentach kodu, gdy nie ma wywołania funkcji, które sa˛ punktami
odwołania.
zakończenie działania watku
˛
w momencie odebrania żadania
˛
odwołania.
PTHREAD_CANCEL_DEFERRED - utrzymujacy
˛ działanie watku,
˛
aż do
momentu osiagni
˛ ecia
˛
pierwszego z punktów odwołania.
Domyślnie (PTHREAD_CANCEL_ENABLE i
PTHREAD_CANCEL_DEFERRED).
Wykład 2 – p. 39/48
Wykład 2
Jednokrotna inicjalizacja
Identyfikatory watków
˛
Funkcja pthread_self zwraca unikalny identyfikator watku
˛
przydzielony mu przez system:
Wywołanie funkcji pthread_once daje pewność, ze inicjalizacja
bedzie
˛
wykonana tylko co najwyżej raz.
pthread_t pthread_self(void);
int pthread_once(pthread_once_t *once_control,
void (*init_routine) (void));
Do porównania identyfikatorów dwóch watków
˛
służy funkcja:
once_control - zainicjowana zmienna typu pthread_once_t,
init_routine - funkcja jaka ma zostać wykonana.
int pthread_equal(pthread_t thread1, pthread_t thread2);
thread1 - identyfikator pierwszego watku,
˛
thread2 - identyfikator drugiego watku.
˛
Zmienna once_control musi być przed użyciem zainicjowana:
pthread_once_t once_control = PTHREAD_ONCE_INIT;
Funkcja zwraca niezerowa˛ wartość jeśli oba identyfikatory odnosza˛ sie˛ do tego
samego watku,
˛
w przeciwnym wypadku zwraca zero.
Wykład 2
Wykład 2 – p. 41/48
Jednokrotna inicjalizacja - przykład (I)
#include <pthread.h>
#include <iostream.h>
#include <unistd.h>
Jednokrotna inicjalizacja - przykład (II)
int main()
{
pthread_t thread[3];
for (int i = 0; i < 3; i++)
{
if (pthread_create(thread + i, NULL, Thread, NULL))
exit(1);
}
for (int i = 0; i < 3; i++)
pthread_join(thread + i, NULL);
pthread_once_t initControl = PTHREAD_ONCE_INIT;
void Init()
{
std::cout << "Initializacja zostala wykonana "
<< "przez watek o identyfikatorze "
<< pthread_self() << std::endl;
}
return 0;
}
void * Thread(void * arg)
{
std::cout << "Thread - " << pthread_self() << std::endl;
pthread_once(&initControl, Init);
return NULL;
}
Wynik działania programu:
Thread - 1026
Initializacja zostala wykonana przez watek o identyfikatorze 1026
Thread - 2051
Thread - 3074
Wykład 2 – p. 43/48
Wykład 2
Jednokrotna inicjalizacja - petla
˛
Bramki
bool init = true;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Poczawszy
˛
od wersji IEEE Std 1003.1-2001 standardu wprowadzono
funkcje implementujace
˛ bramki (barrier).
void* run(void * arg)
{
...
Biblioteka glibc posiada implementacje bramek poczawszy
˛
od wersji
2.2.
pthread_mutex_lock(&mutex);
if (!init)
{
...
// kod wykonywany tylko przez jeden watek
˛
init = false;
}
else
init = true;
pthread_mutex_unlock(&mutex);
...
}
Wykład 2
Wykład 2 – p. 45/48
Bramki - implementacja (I)
Bramki - implementacja (II)
struct pthread_barrier_t
{
int nThreads;
// liczba watkow do wstrzymywania
pthread_mutex_t mutex;
pthread_cond_t cond;
int nWaiting;
// liczba aktualnie czekajacych watkow
};
inline void pthread_barrier_destroy(pthread_barrier_t* barrier)
{
pthread_mutex_destroy(&barrier->mutex);
pthread_cond_destroy(&barrier->cond);
}
inline int pthread_barrier_init(pthread_barrier_t* barrier, void*,
int nThreads)
{
barrier->nThreads = nThreads;
barrier->nWaiting = nThreads - 1;
pthread_mutex_init(&barrier->mutex, NULL);
pthread_cond_init(&barrier->cond, NULL);
return 0;
}
Wykład 2 – p. 47/48
inline void pthread_barrier_wait(pthread_barrier_t* barrier)
{
pthread_mutex_lock(&barrier->mutex);
if (barrier->nWaiting)
{
barrier->nWaiting--;
pthread_cond_wait(&barrier->cond, &barrier->mutex);
}
else
{
barrier->nWaiting = barrier->nThreads - 1;
pthread_cond_broadcast(&barrier->cond);
}
pthread_mutex_unlock(&barrier->mutex);
}
Wykład 2