Unix: programowanie z użyciem w atków

Podobne dokumenty
Unix: programowanie z użyciem w atków

w zależności od wartości zmiennej operacja (dodatnia lub ujemna).

Procesy wielowatkowe. Watki. Wspólne dane watków. Prywatne dane watków. Programowanie z użyciem watków

Watki. kod programu za ladowany do pami eci operacyjnej, wraz ze środowiskiem (zbiorem

Watki. kod programu za ladowany do pami eci operacyjnej, wraz ze środowiskiem (zbiorem

Temat zajęć: Tworzenie i obsługa wątków.

w odróżnieniu od procesów współdzielą przestrzeń adresową mogą komunikować się za pomocą zmiennych globalnych

Plan wykładu. Obliczenia równoległe w zagadnieniach inżynierskich. Wykład 1 p. Wzajemne wykluczanie. Procesy współbieżne

Programowanie aplikacji równoległych i rozproszonych. Wykład 1

Programowanie Współbieżne

Plan wykładu. Programowanie aplikacji równoległych i rozproszonych. Wykład 1 p. Wzajemne wykluczanie. Procesy współbieżne

1. Uruchom poniższy program tworzący pojedynczy wątek:

z powielaniem wielu struktur danych oraz komunikacja

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Funkcje systemu Unix

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.4

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Mariusz Rudnicki PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.4

Podstawy programowania współbieżnego. 1. Wprowadzenie. 2. Podstawowe pojęcia

4.2 Sposób korzystania z l acza

Jędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 1. Wątki

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

pami eć operacyjna przechowuje dane do przetworzenia, tymczasowe dane pomocnicze,

Wielowątkowy serwer TCP

procesy odrębne dzielone

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

SUMA KONTROLNA (icmp_cksum) NUMER KOLEJNY (icmp_seq)

UŻYCIE I ZARZĄDZANIE WĄTKAMI

Problemy współbieżności

UŻYCIE I ZARZĄDZANIE WĄTKAMI

Systemy Operacyjne Ćwiczenia

Problem producentakonsumenta

Sygnały. 7. Sygnały (2005/2006)

J. Ułasiewicz Programowanie aplikacji współbieżnych 1

Systemy Operacyjne - Operacje na plikach

Zaawansowane programowanie w C++

Wątki. S. Samolej: Wątki 1

Tworzenie programów równoległych cd. Krzysztof Banaś Obliczenia równoległe 1

Functionalization. Funkcje w C. Marcin Makowski. 30 listopada Zak lad Chemii Teoretycznej UJ

Procesy. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 9 października 2011

Tworzenie wątków. #include <pthread.h> pthread_t thread;

Problemy czytelników i pisarzy oraz 5 ucztujących filozofów

sposób wykonywania operacji zapisu i odczytu dane odczytywane z l acza usuwane (nie można ich odczytać ponownie),

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

POSIX: IEEE Std (Issue 6, 2004 edition)

Laboratorium Systemów Operacyjnych. Ćwiczenie 4. Operacje na plikach

Systemy Operacyjne 2: Wątki pthreads. dr inż. Arkadiusz Chrobot

przypadków wywo lanie systemowe (funkcja systemowa) lub funkcja biblioteczna zwraca wartość 1(czasamiNULL) iprzypisujezmiennej zewn etrznej,

Pobieranie argumentów wiersza polecenia

9. Problem wzajemnego wykluczania i sekcji krytycznej

Instytut Teleinformatyki

Implementacje zgodne z tym standardem są nazywane wątkami POSIX lub Pthreads.

Rys. 9-1 Procesy P1 i P2 komunikuję się poprzez wspólny obszar. pamięci.

Instrukcja do laboratorium Systemów Operacyjnych. (semestr drugi)

10. Synchronizacja użycia zasobów, Semafory Problem producenta i konsumenta

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Zaawansowane programowanie w C++ (PCP)

Tworzenie programów równoległych. Krzysztof Banaś Obliczenia równoległe 1

eć dzielona standardu POSIX

Functionalization. Jeszcze o funkcjach i strukturze projektu. Marcin Makowski. 3 grudnia Zak lad Chemii Teoretycznej UJ

ZAJECIA Z SYSTEMÓW OPERACYJNYCH 2 (Programowanie): funkcje POSIX

Dzisiejszy wykład. Klasa string. wersja prosta wersja ze zliczaniem odwołań. Wyjątki Specyfikator volatile Semafory

Wykład 5. Synchronizacja (część II) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

POSIX Threads. Wojciech Muła. marzec 2010 z późniejszymi poprawkami (plik utworzony 21 marca 2011)

# echo przykład przykład. echo przykład

2. Zarządzanie procesami

Pola i metody statyczne

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Łącza nienazwane(potoki) Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi.

przerwany proces móg l zareagować na określone zdarzenie. Można je traktować jako software owe wersje przerwań sprz etowych.

Obsługa plików Procesy

Obsługa plików. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 25 września 2011

Tworzenie programów równoległych. Krzysztof Banaś Obliczenia równoległe 1

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

Wielozadaniowość w systemie Microsoft Windows

Funkcje. Piotr Zierhoffer. 7 października Institute of Computer Science Poznań University of Technology

Globalne / Lokalne. Wykład 15. Podstawy programowania (język C) Zmienne globalne / lokalne (1) Zmienne globalne / lokalne (2)

1. Timery i zdarzenia

Model procesu w systemie Linux. Tomasz Borzyszkowski

Łącza nienazwane(potoki)

Architektury systemów równoległych

Modele programowania równoległego. Pamięć współdzielona Rafał Walkowiak dla III roku Informatyki PP

1. Utwórz blok pamięci współdzielonej korzystając z poniższego kodu:

Systemy operacyjne. Zajęcia 11. Monitory

Operacje wejścia/wyjścia (odsłona druga) - pliki

Systemy Operacyjne 1 Laboratorium 2 Procesy i sygnały w Linuksie (jeden tydzień) dr inż. Arkadiusz Chrobot

Zdalne wywołania procedur. Jarosław Kuchta Programowanie Współbieżne

wykład II uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - funkcje, tablice i wskaźniki wykład II dr Jarosław Mederski Spis

Współbieżność w środowisku Java

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Gdy kilka procesów czyta a przynajmniej jeden dokonuje zapisu wynik odczytu zależeć może od sposobu realizacji przeplotu.

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Instytut Teleinformatyki

Paradygmaty programowania. Paradygmaty programowania

Tworzenie programów równoległych. Krzysztof Banaś Obliczenia równoległe 1

Programowanie Proceduralne

J. Ułasiewicz Programowanie aplikacji współbieżnych 1

Transkrypt:

Unix: programowanie z użyciem w atków Witold Paluszyński witold.paluszynski@pwr.wroc.pl http://sequoia.ict.pwr.wroc.pl/ witold/ Copyright c 1999 2006 Witold Paluszyński All rights reserved. Niniejszy dokument zawiera materia ly do wyk ladu na temat programowania z użyciem watków normy POSIX w systemie Unix. Jest on udostepniony pod warunkiem wykorzystania wy l acznie do w lasnych, prywatnych potrzeb i może być kopiowany wy l acznie w ca lości, razem z niniejsza strona tytu low a.

Watki: wprowadzenie Watek (ang. thread) jest sekwencja obliczeń realizowana w ramach jednego procesu. Zestaw watków należacy do jednego procesu wykonuje sie równolegle w ramach jego przestrzeni adresowej. Ponadto watki wspó ldziel a nastepuj ace cechy: identyfikator użytkownika i grupy bieżac a kartoteke dyskowa otwarte pliki handlery sygna lów i ich deklaracje Natomiast każdy watek ma swój unikalny identyfikator, indywidualny zestaw atrybutów, maske sygna lów, priorytet i tzw. konteks obliczeniowy, tzn. stos wywo lań procedur, zestaw rejestrów, itp. Watek tworzy sie funkcja pthread_create() jako wywo lanie jakiejś funkcji użytkownika. 3 4

serwer wieloprocesowy: void serve_client(int sock); serwer wielowatkowy: void *serve_client(void *); void main(void) { int socks, sockc; struct sockaddr_un add; socks=socket(pf_inet, SOCK_STREAM,0); add.sun_family=af_inet;/*...*/ bind(socks, (struct sockaddr *)&add, sizeof(add)); listen(socks, 5); while (1) { sockc = accept(socks,0,0); if (fork() == 0){/*potomek*/ close(socks); serve_client(sockc); exit(0); close(sockc); /*rodzic*/ void main(void) { int socks, sockc; struct sockaddr_un add; pthread_t t; socks=socket(pf_inet, SOCK_STREAM,0); add.sun_family=af_inet;/*...*/ bind(socks, (struct sockaddr *)&add, sizeof(add)); listen(socks, 5); while (1) { sockc = accept(socks,0,0); pthread_create(&t, NULL, serve_client, (void *)&sockc); /*watki nie zamykaja gniazdek*/ /*- sa to wspolne deskryptory*/ 5 Kończenie pracy watków Watek kończy sie gdy: zakończy sie funkcja realizujaca go watek wywo la funkcje pthread_exit() watek zostanie anulowany funkcja pthread_cancel() jeden z watków wykona funkcje exec... zakończy sie proces macierzysty watku W chwili zakończenia watku zwalniane sa jego indywidualne struktury danych (np. stos), ale nie globalne struktury procesu. W chwili zakończenia watki generuja status, który może być odczytany przez atek macierzysty, podobnie jak to jest z procesami. w Jeśli zakończy sie funkcja g lówna watku to skutek jest taki jak gdyby watek wywo la l funkcje pthread_exit() ze zwrócona wartościa jako argumentem. W ten sposób wszystkie jawnie tworzone watki różnia sie od g lównego watku procesu, którego zakończenie funkcji g lównej (main) jest równoważne wywo laniu funkcji exit(). 6

funkcje zwiazane z watkami nie sygnalizuja b ledów przez zmienna errno (która jest globalna w procesie), lecz przez niezerowe wartości funkcji. Jednak dla umożliwienia zwyk lym funkcjom sygnalizacji przyczyn b ledów, każdy z watków MOŻE otrzymać prywatn a kopie zmiennej globalnej errno (wymaga to kompilacji programu z definicja -D_REENTRANT). 7 Watki: status, wcielanie, od l aczanie Kończac prace, watek normalnie pozostawia informacje o swoim stanie funkcja pthread_exit(). Jest to podobne do przekazania statusu wyjścia procesu. Jeden watek może przejść w stan oczekiwania na zakończenie innego watku tego samego procesu. Takie oczekiwanie, nazywane wcielaniem (ang. join), wywo luje sie funkcja pthread_join(). Jest to podobne do oczekiwania na zakończenie podprocesu funkcja waitpid(). Jednak, w odróżnieniu od procesów, watek może być watkiem od l aczonym (ang. detached), co oznacza, że nie może go wcielić inny watek, a po zakończeniu tego watku jest on ostatecznie usuwany, nie czekajac na wcielenie i odebranie jego statusu przez inny w atek. Do dynamicznego od l aczania watków s luży funkcja pthread_detach(). Można też od razu utworzyć watek jako od l aczony, jednak domyślnie tak sie nie dzieje. Stan od l aczenia watku jest jednym z atrybutów watku, o domyślnej wartości odpowiadajacej watkowi nieod l aczonemu. 8

Watki: kasowanie Skasowanie watku bywa przydatne, gdy program uzna, że nie ma potrzeby wykonywać dalej watku, pomimo iż nie zakończy l on jeszcze swej pracy. atek kasuje funkcja pthread_cancel(). W Skasowanie watku może naruszyć spójność modyfikowanych przezeń globalnych danych, zatem powinno być dokonywane w przemyślany sposób. Istnieja mechanizmy wspomagajace oczyszczanie struktur danych (ang. cleanup handlers) po skasowaniu watku. W zwiazku z tym skasowany watek nie kończy sie od razu, lecz w najbliższym punkcie kasowania (ang. cancellation point). Jest to tzw. kasowanie synchroniczne. Punktami kasowania sa wywo lania niektórych funkcji, a program może również stworzyć dodatkowe punkty kasowania wywo luj ac funkcje pthread_testcancel(). W przypadkach gdy watek narusza struktury danych przed jednym z punktów kasowania, może on zadeklarować procedure czyszczac a, która bedzie wykonana automatycznie w przypadku skasowania watku. Te procedure należy oddeklarować natychmiast po przywróceniu spójności struktur danych. 9 Watki: obs luga sygna lów Każdy watek ma w lasn a maske sygna lów. Watek dziedziczy ja od watku, który go zainicjowa l, lecz nie dziedziczy sygna lów czekajacych na odebranie. Sygna l wys lany do procesu jest doreczany do jednego z jego watków. Sygna ly synchroniczne, tzn. takie, które powstaja w wyniku akcji danego watku, np. sigfpe, sigsegv, sa doreczane do watku, który je wywo la l. Sygna l do określonego watku można również skierować funkcja pthread_kill. Sygna ly asynchroniczne, które powstaja bez zwiazku z akcjami, np. sighup, sigint, sa doreczane do jednego z tych watków procesu, które nie maja tego sygna lu zamaskowanego. Jedna ze stosowanych konfiguracji obs lugi sygna lów w programach wielowatkowych jest oddelegowanie jednego z watków do obs lugi sygna lów, i maskowanie ich we wszystkich innych watkach. 10

Atrybuty watków Watki posiadaja zestaw atrybutów, które posiadaja wartości domyślne, automatycznie przyjmowane w chwili tworzenia watku. Możliwe jest utworzenie watku z atrybutami różnymi od domyślnych. Wymaga to utworzenia obiektu artybutów typu pthread_attr_t. Wartości atrybutów watek może również zmieniać w czasie pracy odpowiednimi funkcjami set. 11 Prze l aczanie watków Gdy proces tworzy kilka watków, to moga one być równolegle wykonywane o ile system operacyjny ma wystarczajace zasoby; w przeciwnym wypadku stosowane jest prze l aczanie watków; możliwe jest wybranie strategii prze l aczania i priorytetów watków. W systemie Solaris (Sun Microsystems) watki realizowane sa przez mechanizm niższego poziomu, tak zwane LWP (Light Weight Processes); każdy proces ma do dyspozycji kilka LWP i każdy LWP może realizować jeden watek naraz. Same LWP podlegaja prze l aczaniu przez system operacyjny. 12

Synchronizacja watków (wstep) Ponieważ watki sa z definicji wykonywane wspó lbieżnie i asynchronicznie, a także operuja na wspó ldzielonych globalnych strukturach danych, konieczne sa mechanizmy dla zapewnienia wy l aczności dostepu. Mechanizmy wzajemnej synchronizacji i blokowania ograniczaja wspó lbieżność, zatem należy stosować jak najdrobniejsze blokady. Dostepne mechanizmy synchronizacji standardu POSIX mutexy (ang. mutual exclusion) zmienne warunkowe (ang. conditional variables) blokady odczytu i zapisu (ang. read-write locks) semafory 13 Mechanizmy synchronizacji Mechanizmy synchronizacji zdefiniowane przez norme POSIX wykorzystuja struktury pamieciowe tworzone jawnie w programach, np.: static pthread_mutex_t mutex1; pthread_mutex_t *mutex2; mutex2 = (pthread_mutex_t *) calloc(1, sizeof(pthread_mutex_t)); pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex4; pthread_mutexattr_t attr_ob; pthread_mutexattr_init(&attr_ob) pthread_mutex_init(&mutex4, &attr_ob) Wyzerowany mutex jest otwarty. 14

#define _REENTRANT #include <pthread.h> #define NUM_THREADS 12 Synchronizacja watków (2) #define TRAND(limit,n) \ {struct timeval t;\ gettimeofday(&t,(void *)NULL);\ (n)=rand_r((unsigned *) &t.tv_usec) % (limit)+1; void *thread_main(void *); int Global_data = 0; main(int argc,char * argv[]) { int i; pthread_t thrds[num_threads]; for (i=0; i< NUM_THREADS; i++) pthread_create(&thrds[i], NULL, thread_main, (void *) NULL); for (i=0; i< NUM_THREADS; i++) pthread_join(thrds[i], (void **) NULL); void * thread_main(void *null) { static pthread_mutex_t Global_mutex; /* wyzerowany, tzn. otwarty */ {int n; TRAND(NUM_THREADS,n); sleep(n); 15 pthread_mutex_lock(&global_mutex); Global_data++; printf("watek %3d, proces %d, dane globalne %d\n", pthread_self(), getpid(), Global_data); pthread_mutex_unlock(&global_mutex); return NULL; 16

#define _REENTRANT #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #define NUM_THREADS 12 Synchronizacja watków (3) #define TRAND(limit,n) \ {struct timeval t;\ gettimeofday(&t,(void *)NULL);\ (n)=rand_r((unsigned *) &t.tv_usec) % (limit)+1; void *thread_main(void *); pthread_mutex_t *Global_mutexp; int g_shmid, *Global_datap; main(int argc, char *argv[]) { pthread_mutexattr_t attr_obj; int i, s; 17 /* utw.obszaru pamieci wspolnej mutexu i danych */ g_shmid = shmget(ipc_private, sizeof(int)+sizeof(pthread_mutex_t), IPC_CREAT 0666); Global_datap = (int *) shmat(g_shmid, 0, 0); Global_mutexp = (pthread_mutex_t *) (Global_datap + sizeof(int)); /* zainicj.mutexu w pam.wspolnej struk.atrybutowa */ pthread_mutexattr_init(&attr_obj); pthread_mutexattr_setpshared(&attr_obj, PTHREAD_PROCESS_SHARED); pthread_mutex_init(global_mutexp, &attr_obj); *Global_datap = 0; for (i=0; i< NUM_THREADS; i++) /* odpal.podproces.*/ if ( fork() == 0 ){ thread_main((void *) NULL); exit(2); while((s = (int)wait(null)) && s!= -1); shmdt((char *) Global_datap); shmctl(g_shmid, IPC_RMID, (struct shmid_ds *) 0); exit(0); 18

void * thread_main(void *null) { {int n; TRAND(NUM_THREADS,n); sleep(n); if (0!=pthread_mutex_trylock(Global_mutexp)) { printf("watek %2d, proces %5d, czeka...\n",pthread_self(),getpid()); pthread_mutex_lock(global_mutexp); (*Global_datap)++; printf("watek %2d, proces %5d, dane globalne %d\n", pthread_self(), getpid(), *Global_datap); pthread_mutex_unlock(global_mutexp); return NULL; Przyk lad dzia la podobnie jak poprzedni, tylko nie tworzy watków, a podprocesy, uzyskujace dostep do wspólnego mutexu przez dostepny wszystkim obszar pamieci wspólnej, mieszczacy dane globalne i mutex. UWAGA: Linux nie obs luguje atrybutu PSHARED pthread_mutexattr_setpshared() 19 #define _REENTRANT #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <pthread.h> Synchronizacja watków (4) #define TRAND(limit,n) {struct timeval t;\ gettimeofday(&t,(void *)NULL);\ (n)=rand_r((unsigned *) &t.tv_usec) % (limit)+1; void create_shared_file(); void *thread_main(void *); #define GLOBAL_FILE "SHARED_FILE" #define GLOBAL_SIZE sizeof(int)+sizeof(pthread_mutex_t) pthread_mutex_t *Global_mutexp; int *Global_datap; int gfd; main(int argc, char * argv[]) { 20

if ((argc>1) && (0==strncasecmp(argv[1],"init",4))) { create_shared_file(); exit(0); else if (0!=access(GLOBAL_FILE, F_OK)) { printf("pierwsze wywolanie musi podac argv[1]=init\n"); exit(-1); while((gfd = open(global_file, O_RDWR)) == -1) sleep(1); Global_datap = (int *) mmap(null, GLOBAL_SIZE, PROT_READ PROT_WRITE, MAP_SHARED, gfd, 0); Global_mutexp = (pthread_mutex_t *) (Global_datap + sizeof(int)); thread_main((void *)NULL); close(gfd); exit(0); /* main */ void create_shared_file() { static char zeroed[global_size]; pthread_mutexattr_t attr_obj; 21 gfd = creat(global_file, O_CREAT O_RDWR 0644); write(gfd, zeroed, GLOBAL_SIZE); close(gfd); chmod(global_file, S_IRWXU S_IWGRP S_IRGRP S_IROTH); gfd = open(global_file, O_RDWR); Global_datap = (int *) mmap(null, GLOBAL_SIZE, PROT_READ PROT_WRITE, MAP_SHARED, gfd, 0); *Global_datap = 0; Global_mutexp = (pthread_mutex_t *) (Global_datap + sizeof(int)); pthread_mutexattr_init(&attr_obj); pthread_mutexattr_setpshared(&attr_obj,pthread_process_shared); pthread_mutex_init(global_mutexp, &attr_obj); close(gfd); void * thread_main(void *null) { {int n; TRAND(12,n); sleep(n); if (0!=pthread_mutex_trylock(Global_mutexp)) { printf("watek %2d, proces %5d, czeka...\n", pthread_self(), getpid()); pthread_mutex_lock(global_mutexp); 22

(*Global_datap)++; printf("watek %2d, proces %5d, dane globalne %d\n", pthread_self(), getpid(), *Global_datap); pthread_mutex_unlock(global_mutexp); return NULL; Ten przyk lad również udostepnia mutexy wielu procesom, jednak odwzorowuje obszar pamieci mutexu do pliku, dzieki czemu możliwy jest globalny dostep do mutexu z dowolnego procesu. 23 Przyk lad: producenci i konsumenci przyk lad producentów i konsumenta (jednego) #define MAXNITEMS 1000000 #define MAXNTHREADS 100 int nitems; /* read-only by producer and consumer */ struct { pthread_mutex_t mutex; int buff[maxnitems]; int nput; int nval; shared = { PTHREAD_MUTEX_INITIALIZER ; void *produce(void *), *consume(void *); 24

int main(int argc, char **argv) { int i, nthreads, count[maxnthreads]; pthread_t tid_produce[maxnthreads], tid_consume; if (argc!= 3) err_quit("usage: prodcons2 <#items> <#threads>"); nitems = min(atoi(argv[1]), MAXNITEMS); nthreads = min(atoi(argv[2]), MAXNTHREADS); Set_concurrency(nthreads); /* start all the producer threads */ for (i = 0; i < nthreads; i++) { count[i] = 0; Pthread_create(&tid_produce[i], NULL, produce, &count[i]); /* wait for all the producer threads */ for (i = 0; i < nthreads; i++) { Pthread_join(tid_produce[i], NULL); printf("count[%d] = %d\n", i, count[i]); 25 /* start, then wait for the consumer thread */ Pthread_create(&tid_consume, NULL, consume, NULL); Pthread_join(tid_consume, NULL); exit(0); 26

void * produce(void *arg) { for ( ; ; ) { Pthread_mutex_lock(&shared.mutex); if (shared.nput >= nitems) { Pthread_mutex_unlock(&shared.mutex); return(null); /* array is full, we re done */ shared.buff[shared.nput] = shared.nval; shared.nput++; shared.nval++; Pthread_mutex_unlock(&shared.mutex); *((int *) arg) += 1; void * consume(void *arg) { int i; for (i = 0; i < nitems; i++) { if (shared.buff[i]!= i) printf("buff[%d] = %d\n", i, shared.buff[i]); 27 return(null); 28

Przyk lad: producenci i konsumenci (2) wersja z konsumentem pracujacym równolegle int main(int argc, char **argv) { int i, nthreads, count[maxnthreads]; pthread_t tid_produce[maxnthreads], tid_consume; if (argc!= 3) err_quit("usage: prodcons <#items> <#threads>"); nitems = min(atoi(argv[1]), MAXNITEMS); nthreads = min(atoi(argv[2]), MAXNTHREADS); Set_concurrency(nthreads + 1); /* create all producers and one consumer */ for (i = 0; i < nthreads; i++) { count[i] = 0; Pthread_create(&tid_produce[i], NULL, produce, &count[i]); Pthread_create(&tid_consume, NULL, consume, NULL); 29 /* wait for all producers and the consumer */ for (i = 0; i < nthreads; i++) { Pthread_join(tid_produce[i], NULL); printf("count[%d] = %d\n", i, count[i]); Pthread_join(tid_consume, NULL); exit(0); 30

void * produce(void *arg) { for ( ; ; ) { Pthread_mutex_lock(&shared.mutex); if (shared.nput >= nitems) { Pthread_mutex_unlock(&shared.mutex); return(null); /* array is full, we re done */ shared.buff[shared.nput] = shared.nval; shared.nput++; shared.nval++; Pthread_mutex_unlock(&shared.mutex); *((int *) arg) += 1; void consume_wait(int i) { for ( ; ; ) { Pthread_mutex_lock(&shared.mutex); if (i < shared.nput) { Pthread_mutex_unlock(&shared.mutex); return; /* an item is ready */ Pthread_mutex_unlock(&shared.mutex); void * consume(void *arg) { 31 int i; for (i = 0; i < nitems; i++) { consume_wait(i); if (shared.buff[i]!= i) printf("buff[%d] = %d\n", i, shared.buff[i]); return(null); 32

Synchronizacja watków: zmienne warunkowe do czekania na, i sygnalizowania, spe lnienia jakichś warunków s luż a zmienne warunkowe int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); algorytm dzia lania funkcji pthread_cond_wait: 1. odblokowanie mutexu podanego jako argument wywo lania 2. uśpienie watku do czasu aż jakiś inny watek wywo la funkcje pthread_cond_signal na danej zmiennej warunkowej 3. ponowne zablokowanie mutexu możliwe jest zjawisko spontanicznego obudzenia, tzn. powrotu z funkcji pthread_cond_wait bez wywo lania pthread_cond_signal; należy sie z tym liczyć i sprawdzać poprawność stanu zmiennych po przebudzeniu 33 Synchronizacja watków: zmienne warunkowe (cd.) ogólnie schemat użycia zmiennej warunkowej ze stowarzyszonym mutexem jest nastepujacy: struct { pthread_mutex_t mutex; pthread_cond_t cond; {... inne zmienne przechowujace warunek globvar = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, {... ; schemat sygnalizacji zmiennej warunkowej: pthread_mutex_lock(&globvar.mutex); { kod ustawiajacy warunek oczekiwany przez CV pthread_cond_signal(&globvar.cond); pthread_mutex_unlock(&globvar.mutex); schemat sprawdzania warunku i oczekiwania: pthread_mutex_lock(&globvar.mutex); while ( { warunek niespelniony ) 34

pthread_cond_wait(&globvar.cond, &globvar.mutex); { kod wykorzystujacy oczekiwany warunek pthread_mutex_unlock(&globvar.mutex); 35 Przyk lad: producenci i konsumenci (3) int buff[maxnitems]; struct { pthread_mutex_t mutex; pthread_cond_t cond; int nput; int nval; int nready; nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER ; 36

void * produce(void *arg) { for ( ; ; ) { Pthread_mutex_lock(&nready.mutex); if (nready.nput >= nitems) { Pthread_mutex_unlock(&nready.mutex); return(null); /* array is full, we re done */ buff[nready.nput] = nready.nval; nready.nput++; nready.nval++; nready.nready++; Pthread_cond_signal(&nready.cond); Pthread_mutex_unlock(&nready.mutex); *((int *) arg) += 1; void * consume(void *arg) { int i; for (i = 0; i < nitems; i++) { Pthread_mutex_lock(&nready.mutex); while (nready.nready == 0) 37 Pthread_cond_wait(&nready.cond, &nready.mutex); nready.nready--; Pthread_mutex_unlock(&nready.mutex); if (buff[i]!= i) printf("buff[%d] = %d\n", i, buff[i]); return(null); 38

Przyk lad: producenci i konsumenci (4) int buff[maxnitems]; struct { pthread_mutex_t mutex; int nput; /* next index to store */ int nval; /* next value to store */ put = { PTHREAD_MUTEX_INITIALIZER ; struct { pthread_mutex_t mutex; pthread_cond_t cond; int nready; /* number ready for consumer */ nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER ; 39 void * produce(void *arg) { for ( ; ; ) { Pthread_mutex_lock(&put.mutex); if (put.nput >= nitems) { Pthread_mutex_unlock(&put.mutex); return(null); /* array is full, we re done */ buff[put.nput] = put.nval; put.nput++; put.nval++; Pthread_mutex_unlock(&put.mutex); Pthread_mutex_lock(&nready.mutex); if (nready.nready == 0) Pthread_cond_signal(&nready.cond); nready.nready++; Pthread_mutex_unlock(&nready.mutex); *((int *) arg) += 1; void * consume(void *arg) { int i; for (i = 0; i < nitems; i++) { Pthread_mutex_lock(&nready.mutex); 40

while (nready.nready == 0) Pthread_cond_wait(&nready.cond, &nready.mutex); nready.nready--; Pthread_mutex_unlock(&nready.mutex); if (buff[i]!= i) printf("buff[%d] = %d\n", i, buff[i]); return(null); 41