Comments
Description
Transcript
Sincronizzazione di thread POSIX
Sincronizzazione di thread POSIX Sincronizzazione ● I thread condividono la memoria ● Rischio di race condition ● Necessari meccanismi di sincronizzazione – mutex (semaforo binario) – condition variable I mutex ● ● Un mutex è un semaforo binario (rosso o verde) Un mutex è mantenuto in una struttura pthread_mutex_t ● Va allocata e inizializzata una tale struttura ● Per inizializzare: – se la struttura è allocata staticamente: pthread_mutex_t c = PTHREAD_MUTEX_INITIALIZER – se la struttura è allocata dinamicamente: chiamare pthread_mutex_init (vedere dopo) Inizializzare e distruggere un mutex int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); ● inizializza e distrugge un mutex, rispettivamente ● restituiscono 0 se OK, un codice d'errore altrimenti ● attr può essere NULL (attributi di default) 4 Usare i mutex int pthread_mutex_lock (pthread_mutex_t *mutex); int pthread_mutex_trylock (pthread_mutex_t *mutex); int pthread_mutex_unlock (pthread_mutex_t *mutex); ● acquisiscono e rilasciano il semaforo ● restituiscono 0 se OK, un codice d'errore altrimenti ● se il semaforo è occupato... – ...lock blocca il thread finché il semaforo si libera – ...trylock invece restituisce subito l'errore EBUSY 5 Deadlock ● ● Condizione di attesa ciclica Soluzione base: acquisire i mutex sempre nello stesso ordine 6 Precauzioni ● Non si può acquisire un mutex più di una volta – ● Non si può rilasciare un mutex che non si possiede – ● se chiamo lock su un mutex che già posseggo, vado in deadlock se chiamo unlock su un mutex che non posseggo, il comportamento non è specificato Queste regole si possono cambiare usano gli attributi del mutex 7 Esercizio 1 ● Eliminare le race condition dall'Esercizio 1 relativo ai thread POSIX 8 Le condition variable ● ● Servono per attendere che una condizione si verifichi, escludendo race conditions Una condition variable è mantenuta in una struttura pthread_cond_t ● Va allocata e inizializzata una tale struttura ● Per inizializzare: – se la struttura è allocata staticamente: pthread_cond_t c = PTHREAD_COND_INITIALIZER – se la struttura è allocata dinamicamente: chiamare pthread_cond_init (vedere dopo) 9 Inizializzare e distruggere una condition variable int pthread_cond_init( pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_destroy(pthread_cond_t *cond); ● inizializza e distrugge una condition variable, rispettivamente ● restituiscono 0 se OK, un codice d'errore altrimenti ● attr può essere NULL (attributi di default) 10 Usare una condition variable ● thread che aspetta una condizione ● thread che rende la condizione vera mutex_lock(m) mutex_lock(m) while (condizione falsa) rendi la condizione vera cond_wait(c, m) fa' qualcosa mutex_unlock(m) cond_broadcast(c) mutex_unlock(m) 11 Attendere una condition variable int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t*mutex); ● attende che cond sia segnalata come vera ● restituisce 0 se OK, un codice d'errore altrimenti ● il mutex protegge la condizione – deve essere acquisito prima di chiamare cond_wait – durante l'attesa, cond_wait rilascia il mutex – finita l'attesa, cond_wait riprende il mutex 12 Segnalare una condition variable int pthread_cond_broadcast(pthread_cond_t *cond); ● segnala che la condizione cond è vera ● sblocca tutti i thread che stanno aspettando la condizione ● restituisce 0 se OK, un codice d'errore altrimenti ● va chiamata mentre si possiede il mutex associato 13 Esercizio 2 ● Usando mutex e condition variable, implementare un semaforo con la seguente interfaccia: – sem_init(int n) inizializza il semaforo con valore n – sem_wait() decrementa il semaforo (bloccandosi se il valore e' zero) – sem_post() incrementa il semaforo di 1 14 Esercizio 3 ● ● ● ● realizzare un programma che accetta da riga di comando due numeri interi n ed m, e crea n produttori ed m consumatori produttori e consumatori condividono un array di 100 interi ogni produttore aspetta un numero casuale di secondi tra 1 e 10, e poi produce (cioè inserisce nell'array) da 1 a 5 numeri casuali. Se il produttore trova l'array pieno, salta il turno ogni consumatore aspetta che ci sia un numero da consumare, e poi stampa a video il proprio tid e il valore consumato 15 Dati globali di un thread ● ● Come fa un thread ad avere dati globali? Non può usare una variabile globale, perché condivisa – ● ● ci vorrebbe una variabile globale per ogni thread: complicato Deve usare una variabile locale, che viene passata a tutte le funzioni chiamate dal thread Oppure...threadspecific data 16 Threadspecific data ● associare a una stessa chiave dati diversi per ciascun thread thread principale pthread_key_t chiave; // variabile globale inizializza la chiave crea i thread thread 1 pthread_setspecific(chiave, malloc(...)); ... void *p = pthread_getspecific(chiave) thread 2 pthread_setspecific(chiave, malloc(...)); ... void *p = pthread_getspecific(chiave) 17 Creare una chiave int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); ● crea una chiave per dati privati ● key è l'indirizzo della chiave da inizializzare ● ● destructor è un puntatore alla funzione che deve essere chiamata alla terminazione del thread restituisce 0 se OK, un codice d'errore altrimenti 18 Usare una chiave int pthread_setspecific(pthread_key_t *key, const void* val); ● associa l'indirizzo val alla chiave key, per il thread corrente ● restituisce 0 se OK, un codice d'errore altrimenti void* pthread_getspecific(pthread_key_t *key); ● restituisce l'indirizzo associato alla chiave key nel thread corrente – restituisce NULL se nessun indirizzo è stato associato a key 19 Esercizio 4 ● ● ● scrivere un programma che prende da riga di comando un intero n, imposta un gestore per il segnale SIGUSR1 e poi crea n thread ciascun thread aspetta un numero casuale di secondi tra 1 e 5, poi invia il segnale SIGUSR1 a uno a caso tra i suoi fratelli thread (e poi ripete il procedimento) il gestore del segnale stampa a video il tid del thread corrente e il numero totale di segnali SIGUSR1 ricevuti da quel thread fino a quel momento 20