Thread POSIX
Transcript
Thread POSIX
Thread POSIX Introduzione ai thread POSIX Thread POSIX: aspetti preliminari operazioni elementari sui thread Sincronizzazione Semafori semafori di mutua esclusione semafori generali utilizzo ed esempi Variabili condition generalità utilizzo ed esempi Thread Thread POSIX Standard ANSI/IEEE POSIX 1003.1 (1990) Thread thread POSIX (pthread) insieme di istruzioni che viene eseguito in modo indipendente rispetto al main Utilizzo includere l’header della libreria1 Stato di un thread #include <pthread.h> stack, registri, proprietà di scheduling, stato dei segnali, dati privati compilare specificando la libreria2 gcc <opzioni> -pthread Vantaggi 1 concorrenza limitato uso di risorse Programmazione concorrente con i thread POSIX 2 4 per poter interpretare correttamente i messaggi di errore è necessario anche includere l’header <errno.h> per ulteriori informazioni sulla compilazione fare riferimento alla documentazione della piattaforma utilizzata man pthread (o man pthreads) Programmazione concorrente con i thread POSIX 5 Risorse Risorse (1 di 2) POSIX Threads Programming Tutorial Manpages http://www.llnl.gov/computing/tutorials/pthreads/ pacchetto manpages-posix-dev (Debian) Libri (consultazione) B. Lewis, D. Berg, “Threads Primer”, Prentice Hall D. Butenhof, “Programming With POSIX Threads”, Addison Wesley B. Nichols et al, “Pthreads Programming”, O’Reilly Programmazione concorrente con i thread POSIX (2 di 2) 6 man pthread.h man <nomefunzione> Manuale GNU libc http://www.gnu.org/software/libc/manual/html_node/ POSIX-Threads.html Programmazione concorrente con i thread POSIX 7 Tipi definiti nella libreria pthread Gestione dei thread All’interno di un programma un thread è rappresentato da un identificatore tipo opaco pthread_t pthread_t pthread_t pthread_self( pthread_self( void void )) int int pthread_equal( pthread_equal( pthread_t pthread_t t1, t1, pthread_t pthread_t t2 t2 )) Attributi di un thread tipo opaco pthread_attr_t Programmazione concorrente con i thread POSIX 9 Creazione di un thread Creazione di un thread (1 di 2) (2 di 2) int int pthread_create( pthread_create( pthread_t pthread_t *thread, *thread, const pthread_attr_t const pthread_attr_t *attr, *attr, void void *(*start_routine)(void *(*start_routine)(void *), *), void void *arg *arg )) int int pthread_create( pthread_create( pthread_t pthread_t *thread, *thread, const pthread_attr_t const pthread_attr_t *attr, *attr, void void *(*start_routine)(void *(*start_routine)(void *), *), void void *arg *arg )) pthread_t *thread void *arg identificatore del thread (se creato con successo) argomento da passare al thread const pthread_attr_t *attr Valore di ritorno attributi del processo da creare se NULL usa valori default 0 in assenza di errore diverso da zero altrimenti void *(*start_routine)(void *) attributi errati mancanza di risorse funzione da eseguire alla creazione del thread Programmazione concorrente con i thread POSIX 10 Terminazione di un thread Programmazione concorrente con i thread POSIX Esempio: creazione e terminazione (1 di 2) void void pthread_exit( pthread_exit( void void *value_ptr *value_ptr )) /* /* Include Include */ */ #include #include <pthread.h> <pthread.h> void *value_ptr #include #include <stdio.h> <stdio.h> #define #define NUM_THREADS NUM_THREADS valore di ritorno del thread (può essere ottenuto attraverso la funzione join) 55 /* /* Corpo Corpo del del thread thread */ */ void *PrintHello(void void *PrintHello(void *num) *num) {{ Altre possibilità printf("\n%d: printf("\n%d: Hello Hello World!\n", World!\n", num); num); pthread_exit(NULL); pthread_exit(NULL); ritorna dalla funzione chiamante cancellazione da parte di un altro thread terminazione dell’intero processo Programmazione concorrente con i thread POSIX 11 }} Continua 12 Programmazione concorrente con i thread POSIX 13 Esempio: creazione e terminazione (2 di 2) Passaggio parametri /* /* Programma Programma */ */ int int main main (int (int argc, argc, char char *argv[]) *argv[]) Per riferimento con un cast a void* Esempio (errato) {{ pthread_t pthread_t threads[NUM_THREADS]; threads[NUM_THREADS]; int int rc, rc, t; t; il ciclo modifica il contenuto dell’indirizzo passato come parametro for(t=0; for(t=0; t<NUM_THREADS; t<NUM_THREADS; t++){ t++){ printf("Creating printf("Creating thread thread %d\n", %d\n", t); t); int int rc, rc, t; t; rc rc == pthread_create(&threads[t], pthread_create(&threads[t], NULL, NULL, PrintHello, PrintHello, (void (void *)t); *)t); if (rc){ if (rc){ for(t=0; for(t=0; t<NUM_THREADS; t<NUM_THREADS; t++) t++) {{ printf("Creating printf("Creating thread thread %d\n", %d\n", t); t); printf("ERROR; printf("ERROR; return return code code from from pthread_create() pthread_create() is is %d\n", %d\n", rc); rc); exit(-1); exit(-1); rc rc == pthread_create(&threads[t], pthread_create(&threads[t], NULL, NULL, PrintHello, PrintHello, (void *) &t); (void *) &t); }} }} pthread_exit(NULL); pthread_exit(NULL); ... ... }} }} Programmazione concorrente con i thread POSIX Passaggio parametri 14 Programmazione concorrente con i thread POSIX (2 di 2) Esempio (corretto) Sincronizzazione struttura dati univoca per ogni thread int int *taskids[NUM_THREADS]; *taskids[NUM_THREADS]; for(t=0; for(t=0; t<NUM_THREADS; t<NUM_THREADS; t++){ t++){ taskids[t] taskids[t] == (int (int *) *) malloc(sizeof(int)); malloc(sizeof(int)); *taskids[t] = t; *taskids[t] = t; printf("Creating printf("Creating thread thread %d\n", %d\n", t); t); rc rc == pthread_create(&threads[t], pthread_create(&threads[t], NULL, NULL, PrintHello, PrintHello, (void (void *) *) taskids[t]); taskids[t]); ... ... }} Programmazione concorrente con i thread POSIX (1 di 2) 16 15 Join tra thread Operazione di join Forma elementare di sincronizzazione int int pthread_join( pthread_join( pthread_t pthread_t *thread, *thread, void void **value **value )) il thread che effettua il join si blocca finché uno specifico thread non termina il thread che effettua il join può ottenere lo stato del thread che termina pthread_t *thread identificatore del thread di cui attendere la terminazione void **value valore restituito dal thread che termina Attributo detachstate di un thread Valore di ritorno 0 in caso di successo EINVAL se il thread da attendere non è joinable ESRCH se non è stato trovato nessun thread corrispondente all’identificatore specificato specifica se si può invocare o no la funzione join su un certo thread un thread è joinable per default Programmazione concorrente con i thread POSIX 18 Impostazione attributo di join Programmazione concorrente con i thread POSIX Esempio: thread join int int pthread_attr_init( pthread_attr_init( pthread_attr_t pthread_attr_t *attr *attr )) int int pthread_attr_setdetachstate( pthread_attr_setdetachstate( pthread_attr_t pthread_attr_t *attr, *attr, int int detachstate detachstate )) int int pthread_attr_destroy( pthread_attr_destroy( pthread_attr_t pthread_attr_t *attr *attr )) int int main main (int (int argc, argc, char char *argv[]) *argv[]) {{ pthread_t pthread_t thread[NUM_THREADS]; thread[NUM_THREADS]; ... ... pthread_attr_destroy(&attr); pthread_attr_destroy(&attr); for(t=0; for(t=0; t<NUM_THREADS; t<NUM_THREADS; t++) t++) {{ rc rc == pthread_join(thread[t], pthread_join(thread[t], (void (void **)&status); **)&status); if (rc) { if (rc) { printf("ERROR; printf("ERROR; return return code code from from pthread_join() pthread_join() is is %d\n", %d\n", rc); rc); Esempio /* /* Attributo Attributo */ */ pthread_attr_t pthread_attr_t attr; attr; exit(-1); exit(-1); }} printf("Completed printf("Completed join join with with thread thread %d %d status= status= %d\n",t, %d\n",t, status); status); /* /* Inizializzazione Inizializzazione esplicita esplicita dello dello stato stato joinable joinable */ */ pthread_attr_init(&attr); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); PTHREAD_CREATE_JOINABLE); ... ... }} pthread_exit(NULL); pthread_exit(NULL); pthread_attr_destroy(&attr); pthread_attr_destroy(&attr); Programmazione concorrente con i thread POSIX 19 }} 20 Programmazione concorrente con i thread POSIX 21 Sincronizzazione tra thread Attraverso variabili globali Semafori di mutua esclusione condivise tra thread meccanismi di protezione Compito del programmatore corretto utilizzo delle funzioni di sincronizzazione Meccanismi forniti dalla libreria semafori di mutua esclusione semafori generali variabili condition Programmazione concorrente con i thread POSIX 22 Semafori di mutua esclusione (mutex) Uso dei mutex Esempio Protezione delle sezione critiche creazione e inizializzazione di una variabile mutex più thread tentano di accedere alla risorsa invocando l’operazione di lock un solo thread riesce ad acquisire il mutex mentre gli altri si bloccano il thread che ha acquisito il mutex manipola la risorsa lo stesso thread la rilascia invocando la unlock un altro thread acquisisce il mutex e così via variabili condivise modificate da più thread solo un thread alla volta può accedere ad una risorsa protetta da un mutex il mutex è un semaforo binario Interfaccia lock per bloccare una risorsa unlock per liberare una risorsa Programmazione concorrente con i thread POSIX 24 Programmazione concorrente con i thread POSIX 25 Mutex: tipo e inizializzazione statica Mutex: inizializzazione dinamica Nella libreria pthread un mutex è una variabile di tipo pthread_mutex_t Inizializzazione int int pthread_mutex_init( pthread_mutex_init( pthread_mutex_t pthread_mutex_t *mutex, *mutex, const const pthread_mutexattr_t pthread_mutexattr_t *mattr *mattr )) pthread_mutex_t *mutex statica contestuale alla dichiarazione dinamica attraverso la funzione puntatore al mutex da inizializzare pthread_mutexattr_t *mattr pthread_mutex_attr_init() attributi del mutex da creare estensioni per sistemi real time se NULL usa valori default /* /* Variabili Variabili globali globali */ */ pthread_mutex_t pthread_mutex_t amutex amutex == PTHREAD_MUTEX_INITIALIZER; PTHREAD_MUTEX_INITIALIZER; Valore di ritorno sempre il valore 0 Programmazione concorrente con i thread POSIX 26 Mutex: operazioni lock e trylock Programmazione concorrente con i thread POSIX 27 Mutex: unlock e distruzione Due varianti bloccante (standard) non bloccante (utile per evitare deadlock) int int pthread_mutex_unlock( pthread_mutex_unlock( pthread_mutex_t pthread_mutex_t *mutex *mutex )) int int pthread_mutex_destroy( pthread_mutex_destroy( pthread_mutex_t pthread_mutex_t *mutex *mutex )) int int pthread_mutex_lock( pthread_mutex_lock( pthread_mutex_t pthread_mutex_t *mutex *mutex )) pthread_mutex_t *mutex int int pthread_mutex_trylock( pthread_mutex_trylock( pthread_mutex_t pthread_mutex_t *mutex *mutex )) puntatore al mutex da sbloccare/distruggere Valore di ritorno pthread_mutex_t *mutex 0 in caso di successo destroy restituisce EBUSY se il mutex è occupato puntatore al mutex da bloccare Valore di ritorno 0 in caso di successo, diverso da 0 altrimenti trylock restituisce EBUSY se il mutex è occupato Programmazione concorrente con i thread POSIX 28 Programmazione concorrente con i thread POSIX 29 Esempio: uso dei mutex Esempio: uso dei mutex (1 di 2) #include #include <pthread.h> <pthread.h> int int a=1; a=1; b=1; b=1; main() main() {{ pthread_t pthread_t threadid1, threadid1, threadid2; threadid2; pthread_mutex_t pthread_mutex_t mm == PTHREAD_MUTEX_INITIALIZER; PTHREAD_MUTEX_INITIALIZER; void* thread1(void *arg) void* thread1(void *arg) {{ int int ii == 1, 1, j=2; j=2; pthread_create(&threadid1, pthread_create(&threadid1, NULL, NULL, thread1, thread1, (void (void *)&i); *)&i); pthread_mutex_lock(&m); pthread_mutex_lock(&m); printf(“Primo printf(“Primo thread thread (parametro: (parametro: %d)\n", %d)\n", (*arg); (*arg); pthread_create(&threadid2, pthread_create(&threadid2, NULL, NULL, thread2, thread2, (void (void *)&j); *)&j); pthread_join(threadid1, pthread_join(threadid1, NULL); NULL); a++; a++; b++; b++; pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); pthread_join(threadid2, pthread_join(threadid2, NULL); NULL); printf("Valori finali: a=%d printf("Valori finali: a=%d b=%d\n", b=%d\n", a, a, b); b); }} void* void* thread2(void thread2(void *arg) *arg) {{ }} pthread_mutex_lock(&m); pthread_mutex_lock(&m); printf(“Secondo printf(“Secondo thread thread (parametro: (parametro: %d)\n", %d)\n", *arg); *arg); b=b*2; b=b*2; a=a*2; a=a*2; pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); }} (2 di 2) Tratto con modifiche da: http://www.univ.trieste.it/~mumolo/posix2.pdf Continua Programmazione concorrente con i thread POSIX 30 Esempio: inizializzazione dinamica (1 di 2) Programmazione concorrente con i thread POSIX 31 Esempio: inizializzazione dinamica (2 di 2) #include #include <pthread.h> <pthread.h> int a=1; b=1; int a=1; b=1; main() main() {{ pthread_t pthread_t threadid1, threadid1, threadid2; threadid2; pthread_mutex_t pthread_mutex_t m; m; void* thread1(void void* thread1(void *arg) *arg) {{ pthread_mutex_lock(&m); pthread_mutex_lock(&m); printf(“Primo printf(“Primo thread thread (parametro: (parametro: %d)\n", %d)\n", (*arg); (*arg); int int ii == 1, 1, j=2; j=2; pthread_mutex_init(&m, pthread_mutex_init(&m, NULL); NULL); a++; a++; b++; b++; pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); pthread_create(&threadid1, pthread_create(&threadid1, NULL, NULL, thread1, thread1, (void (void *)&i); *)&i); pthread_create(&threadid2, NULL, thread2, (void *)&j); pthread_create(&threadid2, NULL, thread2, (void *)&j); pthread_join(threadid1, pthread_join(threadid1, NULL); NULL); pthread_join(threadid2, NULL); pthread_join(threadid2, NULL); }} void* void* thread2(void thread2(void *arg) *arg) {{ printf("Valori printf("Valori finali: finali: a=%d a=%d b=%d\n", b=%d\n", a, a, b); b); pthread_mutex_destroy(&m); pthread_mutex_destroy(&m); pthread_mutex_lock(&m); pthread_mutex_lock(&m); printf(“Secondo printf(“Secondo thread thread (parametro: (parametro: %d)\n", %d)\n", *arg); *arg); b=b*2; b=b*2; a=a*2; a=a*2; pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); }} Programmazione concorrente con i thread POSIX }} Continua 32 Programmazione concorrente con i thread POSIX 33 Semafori classici (generali) Semafori classici (1 di 2) Semafori il cui valore può essere impostato dal programmatore utilizzati per casi più generali di sincronizzazione esempio: produttore consumatore Interfaccia operazione wait operazione post (signal) Programmazione concorrente con i thread POSIX Semafori classici (generali) Semafori classici: inizializzazione (1 di 2) (2 di 2) Richiedono un’inizializzazione esplicita da parte del programmatore Semafori classici e standard POSIX non presenti nella prima versione dello standard introdotti insieme come estensione real-time con lo standard IEEE POSIX 1003.1b (1993) int int sem_init( sem_init( sem_t sem_t *sem, *sem, int int pshared, pshared, unsigned unsigned int int value value )) sem_t *sem Utilizzo puntatore al semaforo da inizializzare associati al tipo sem_t includere l’header int pshared se 1 il semaforo è condiviso tra processi se 0 il semaforo è privato del processo nell’attuale implementazione deve essere posto a 0 #include <semaphore.h> Programmazione concorrente con i thread POSIX 35 36 Programmazione concorrente con i thread POSIX 37 Semafori classici: inizializzazione (2 di 2) Semafori classici: operazione wait Due varianti bloccante (standard) non bloccante (utile per evitare deadlock) int int sem_init( sem_init( sem_t sem_t *sem, *sem, int int pshared, pshared, unsigned unsigned int int value value )) int int sem_wait( sem_wait( sem_t sem_t *sem *sem )) unsigned int *value int int sem_trywait( sem_trywait( sem_t sem_t *sem *sem )) valore da assegnare al semaforo pthread_mutex_t *mutex Valore di ritorno puntatore al semaforo da decrementare 0 in caso di successo, -1 altrimenti con la variabile errno settata a EINVAL se il semaforo supera il valore Valore di ritorno sempre 0 per la wait semplice trywait restituisce -1 se il semaforo ha valore 0 SEM_VALUE_MAX setta la variabile errno a EAGAIN Programmazione concorrente con i thread POSIX 38 Semafori classici: operazione post e distruzione Programmazione concorrente con i thread POSIX 39 Semafori classici: lettura del valore int int sem_post( sem_post( sem_t sem_t *sem *sem )) int int sem_destroy( sem_destroy( sem_t sem_t *sem *sem )) int int sem_getvalue( sem_getvalue( sem_t sem_t *sem, *sem, int int *sval *sval )) sem_t *sem puntatore al semaforo da incrementare/distruggere sem_t *sem Valore di ritorno puntatore del semaforo di cui leggere il valore 0 in caso di successo, -1 altrimenti con la variabile errno settata in base al tipo di errore int *sval valore del semaforo EINVAL se il semaforo supera il valore SEM_VALUE_MAX dopo l’incremento (sem_post) Valore di ritorno sempre 0 EBUSY se almeno un thread è bloccato sul semaforo (sem_destroy) Programmazione concorrente con i thread POSIX 40 Programmazione concorrente con i thread POSIX 41 Esempio: lettori e scrittori Esempio: lettori e scrittori (1 di 4) #include #include <stdio.h> <stdio.h> #include #include <pthread.h> <pthread.h> (2 di 4) int int main(void) main(void) {{ pthread_t pthread_t s1TID, s1TID, s2TID, s2TID, lTID; lTID; #include #include <semaphore.h> <semaphore.h> #define #define LUN LUN 20 20 int int res, res, i; i; shared.primo shared.primo == shared.ultimo shared.ultimo == 0; 0; #define #define CICLI CICLI 11 #define #define DELAY DELAY 100000 100000 sem_init(&shared.mutex, sem_init(&shared.mutex, 0, 0, 1); 1); sem_init(&shared.piene, sem_init(&shared.piene, 0, 0, 0); 0); struct struct {{ char char scritta[LUN+1]; scritta[LUN+1]; sem_init(&shared.vuote, sem_init(&shared.vuote, 0, 0, LUN); LUN); pthread_create(&lTID, NULL, pthread_create(&lTID, NULL, lettore, lettore, NULL); NULL); /* /* Variabili Variabili per per la la gestione gestione del del buffer buffer */ */ int primo, ultimo; int primo, ultimo; pthread_create(&s1TID, pthread_create(&s1TID, NULL, NULL, scrittore1, scrittore1, NULL); NULL); pthread_create(&s2TID, NULL, scrittore2, NULL); pthread_create(&s2TID, NULL, scrittore2, NULL); /* /* Variabili Variabili semaforiche semaforiche */ */ sem_t mutex, piene, sem_t mutex, piene, vuote; vuote; }} shared; shared; pthread_join(s1TID, pthread_join(s1TID, NULL); NULL); pthread_join(s2TID, NULL); pthread_join(s2TID, NULL); void void *scrittore1(void *scrittore1(void *); *); void void *scrittore2(void *scrittore2(void *); *); void *lettore(void *); void *lettore(void *); pthread_join(lTID, pthread_join(lTID, NULL); NULL); Continua Programmazione concorrente con i thread POSIX 42 Esempio: lettori e scrittori Continua }} Programmazione concorrente con i thread POSIX 43 Esempio: lettori e scrittori (3 di 4) void void *scrittore1(void *scrittore1(void *in) *in) {{ int int i, i, j, j, k; k; (4 di 4) void void *lettore(void *lettore(void *in) *in) {{ int int i, i, j, j, k; k; char char local[LUN+1]; local[LUN+1]; local[LUN] local[LUN] == 0; 0; for for (i=0; (i=0; i<CICLI; i<CICLI; i++) i++) {{ for(k=0; k<LUN; k++) for(k=0; k<LUN; k++) {{ /* /* Buffer Buffer locale locale */ */ for for (i=0; (i=0; i<2*CICLI; i<2*CICLI; i++) i++) {{ for(k=0; k<LUN; k++) for(k=0; k<LUN; k++) {{ sem_wait(&shared.vuote); sem_wait(&shared.vuote); /* /* Controllo Controllo che che il il buffer buffer non non sia sia pieno pieno */ */ sem_wait(&shared.mutex); /* Acquisisco la mutua esclusione */ sem_wait(&shared.mutex); /* Acquisisco la mutua esclusione */ sem_wait(&shared.piene); sem_wait(&shared.piene); /* /* Controllo Controllo che che il il buffer buffer non non sia sia vuoto vuoto */ */ sem_wait(&shared.mutex); /* Acquisisco la mutua esclusione */ sem_wait(&shared.mutex); /* Acquisisco la mutua esclusione */ shared.scritta[shared.ultimo] shared.scritta[shared.ultimo] == '-'; '-'; shared.ultimo shared.ultimo == (shared.ultimo+1)%(LUN); (shared.ultimo+1)%(LUN); local[k] local[k] == shared.scritta[shared.primo]; shared.scritta[shared.primo]; shared.primo shared.primo == (shared.primo+1)%(LUN); (shared.primo+1)%(LUN); sem_post(&shared.mutex); sem_post(&shared.mutex); sem_post(&shared.piene); sem_post(&shared.piene); for(j=0; for(j=0; j<DELAY; j<DELAY; j++); j++); }} }} return return NULL; NULL; }} Programmazione concorrente con i thread POSIX /* /* Operazioni Operazioni sui sui dati dati */ */ /* /* Libero Libero il il mutex mutex */ */ /* /* Segnalo Segnalo l’aggiunta l’aggiunta di di un un carattere carattere */ */ sem_post(&shared.mutex); sem_post(&shared.mutex); sem_post(&shared.vuote); sem_post(&shared.vuote); /* /* ... ... perdo perdo un un po’ po’ di di tempo tempo */ */ Continua 44 for(j=0; for(j=0; j<DELAY; j<DELAY; j++); j++); /* /* Operazioni Operazioni sui sui dati dati */ */ /* /* Libero Libero il il mutex mutex */ */ /* /* Segnalo Segnalo che che ho ho letto letto un un carattere carattere */ */ /* /* ... ... perdo perdo un un po’ po’ di di tempo tempo */ */ }} }} return return NULL; NULL; }} Programmazione concorrente con i thread POSIX 45 Variabili condition Variabili condition Oggetti di sincronizzazione su cui un processo si può bloccare in attesa associate ad una condizione logica arbitraria (predicato) generalizzazione dei semafori nuovo tipo pthread_cond_t attributi variabili condizione di tipo pthread_condattr_t Programmazione concorrente con i thread POSIX Variabili condition: inizializzazione Variabili condition: sincronizzazione Una variabile condition è sempre associata ad un mutex int int pthread_cond_init( pthread_cond_init( pthread_cond_t pthread_cond_t *cond, *cond, pthread_condattr_t *cond_attr ) pthread_condattr_t *cond_attr ) int int pthread_cond_destroy( pthread_cond_destroy( pthread_cond_t pthread_cond_t *cond *cond )) int int pthread_condattr_init( pthread_condattr_init( pthread_condattr_t pthread_condattr_t *attr *attr )) int int pthread_condattr_destroy( pthread_condattr_destroy( pthread_condattr_t pthread_condattr_t *attr) *attr) pthread_cond_ inizializzazione/distruzione variabili condition un thread ottiene il mutex e testa il predicato se il predicato è verificato allora il thread esegue le sue operazioni e rilascia il mutex se il predicato non è verificato, in modo atomico il mutex viene rilasciato (implicitamente) il thread si blocca sulla variabile condition pthread_condattr_ inizializzazione/distruzione attributi condition un thread bloccato riacquisisce il mutex nel momento in cui viene svegliato da un altro thread condivisione della condizione tra più processi Programmazione concorrente con i thread POSIX 47 48 Programmazione concorrente con i thread POSIX 49 Variabili condition: operazione wait Variabili condition: operazione signal Due varianti standard (sblocca un solo thread bloccato) broadcast (sblocca tutti i thread bloccati) int int pthread_cond_wait( pthread_cond_wait( pthread_cond_t pthread_cond_t *cond, *cond, pthread_mutex_t pthread_mutex_t *mutex *mutex )) int int pthread_cond_signal pthread_cond_signal (( pthread_cond_t pthread_cond_t *cond) *cond) pthread_cond_t *cond int int pthread_cond_broadcast pthread_cond_broadcast (( pthread_cond_t pthread_cond_t *cond *cond )) puntatore all’oggetto condizione su cui bloccarsi pthread_mutex_t *mutex pthread_cond_t *cond puntatore all’oggetto mutex da sbloccare puntatore al l’oggetto condizione Valore di ritorno Valore di ritorno sempre 0 Programmazione concorrente con i thread POSIX sempre 0 50 Variabili condition: valutazione Programmazione concorrente con i thread POSIX Esempio: incremento contatore (1 di 3) void void *inc_count(void *inc_count(void *idp) *idp) {{ int int j,i; j,i; double double result=0.0; result=0.0; Il thread svegliato deve rivalutare la condizione l’altro thread potrebbe non aver testato la condizione la condizione potrebbe essere cambiata nel frattempo possono verificarsi wakeup “spuri” int int *my_id *my_id == idp; idp; for (i=0; i<TCOUNT; for (i=0; i<TCOUNT; i++) i++) {{ pthread_mutex_lock(&count_mutex); pthread_mutex_lock(&count_mutex); count++; count++; if if (count (count == == COUNT_LIMIT) COUNT_LIMIT) {{ /* /* Check Check the the value value of of count count and and signal signal */ */ pthread_cond_signal(&count_threshold_cv); pthread_cond_signal(&count_threshold_cv); pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); while(!condition_to_hold) while(!condition_to_hold) }} pthread_mutex_unlock(&count_mutex); pthread_mutex_unlock(&count_mutex); ptread_cond_wait(&cond, ptread_cond_wait(&cond, &mutex); &mutex); computation(); computation(); for for (j=0; (j=0; j<1000; j<1000; j++) j++) result = result + result = result + (double)random(); (double)random(); }} pthread_exit(NULL); pthread_exit(NULL); pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex); }} Programmazione concorrente con i thread POSIX 51 52 Programmazione concorrente con i thread POSIX Continua 53 Esempio: incremento contatore Esempio: incremento contatore (2 di 3) void void *watch_count(void *watch_count(void *idp) *idp) {{ int int main main (int (int argc, argc, char char *argv[]) *argv[]) {{ int int i, i, rc; rc; int int *my_id *my_id == idp; idp; printf("Starting printf("Starting watch_count(): watch_count(): thread thread %d\n", %d\n", *my_id) *my_id) pthread_mutex_lock(&count_mutex); pthread_mutex_lock(&count_mutex); */ */ while while (count<COUNT_LIMIT) (count<COUNT_LIMIT) {{ pthread_t pthread_t threads[3]; threads[3]; pthread_attr_t pthread_attr_t attr; attr; /* /* Initialize Initialize mutex mutex and and condition condition variable variable objects objects */ */ pthread_mutex_init(&count_mutex, NULL); pthread_mutex_init(&count_mutex, NULL); /* /* Lock Lock mutex mutex and and wait wait for for signal. signal. pthread_cond_init pthread_cond_init (&count_threshold_cv, (&count_threshold_cv, NULL); NULL); /* Create threads and wait for termination /* Create threads and wait for termination */ */ pthread_cond_wait(&count_threshold_cv, pthread_cond_wait(&count_threshold_cv, &count_mutex); &count_mutex); printf("watch_count(): printf("watch_count(): thread thread %d %d Condition Condition signal signal ... ... /* /* Clean Clean up up and and exit exit */ */ received.\n", received.\n", *my_id); *my_id); }} pthread_mutex_unlock(&count_mutex); pthread_mutex_unlock(&count_mutex); pthread_exit(NULL); pthread_exit(NULL); }} Programmazione concorrente con i thread POSIX (3 di 3) pthread_attr_destroy(&attr); pthread_attr_destroy(&attr); pthread_mutex_destroy(&count_mutex); pthread_mutex_destroy(&count_mutex); Continua 54 pthread_cond_destroy(&count_threshold_cv); pthread_cond_destroy(&count_threshold_cv); pthread_exit(NULL); pthread_exit(NULL); }} Programmazione concorrente con i thread POSIX 55