Svantaggi legati all'uso di processi
• La gestione tradizionale dei processi può
  diventare molto onerosa dal punto di vi...
Svantaggi legati all'uso di processi
• La nozione classica di processo ingloba due
  concetti ben distinti, gestiti dal SO...
Separazione risorse-esecuzione
• Nei SO moderni, i due concetti ora visti
  possono essere disaccoppiati
  – L'unità base ...
Thread
• Un thread è l'insieme dei dati utilizzati per
  eseguire, in maniera concorrente, più tracce
  relative al codice...
Ambiente a processi (single threaded)
• Ciascun processo      processo
                                               file...
Ambiente multithreaded
• Ciascun processo        processo
                                                    file
  conti...
Processi vs. Thread




Processo           Thread


                            7
Benefici del multithreading
• Reattività
  – Il blocco di un thread non inficia gli altri
  – Maggiore fluidità di esecuzi...
Problemi del multithreading
• Modello di programmazione complesso
  – La condivisione delle risorse e della memoria
    im...
Applicazioni del multithreading
• Quali applicazioni si lasciano implementare
  tramite i thread? Tutte quelle applicazion...
Supporto del SO ai thread
• Il SO deve fornire un supporto per la gestione
  dei thread
  – Supporto allo user level, tram...
Modello Many-to-One (N:1)
• A più supporti user level corrisponde un solo
  supporto kernel level
   – Il kernel vede una ...
Modello Many-to-One (N:1)
                        Create, stop, schedule,
Processo
                           resume, dest...
Modello Many-to-One (N:1)
• Vantaggi
  – Gestione efficientissima dei thread (non viene
    coinvolto lo scheduler del ker...
Modello One-to-One (1:1)
• Ad un supporto user level corrisponde un
  supporto kernel level
  – Il kernel vede una traccia...
Modello One-to-One (1:1)
Processo        Thread       Thread

                   Thread       Thread
     Main
           ...
Modello One-to-One (1:1)
• Vantaggi
  – Se un thread effettua una chiamata bloccante,
    non blocca gli altri thread
  – ...
Modello Many-to-Many (N:M)
• Ad N supporti user level corrispondono M
  supporti kernel level (N>M)
  – Unione del modelli...
Modello Many-to-Many (N:M)
     ProcessoCreate, stop, sched,   ProcessoCreate, stop, sched,
              resume, destroy ...
Quando si applicano i modelli?
• Modello Many-to-One
  – Buono per applicazioni di tipo parallelo con
    pochissimo I/O
 ...
Multithreading: creazione/immagine
• In un programma multithreaded la semantica
  delle operazioni fork() ed exec() cambia...
Multithreading: cancellazione
• La cancellazione è l'operazione di terminazione
  prematura di un thread
• Il thread selez...
Multithreading: gestione segnali
• In ambiente single threaded, il segnale viene
  inviato ad un processo
• In ambiente mu...
Multithreading: thread pool
• All'aumentare del numero di flussi concorrenti,
  le risorse del sistema possono esaurire
  ...
Multithreading: Thread Specific Data
• In particolari circostanze, ciascun thread può
  necessitare di una copia privata d...
Modelli di programmazione
• L'uso dei thread permette l'implementazione
  efficiente di alcuni modelli di programmazione
•...
Modelli di programmazione


Pipeline

                      M

Master-Slave
                  S   S       S



           ...
Librerie di threading
• Ciascuna implementazione del supporto ai
  thread fornisce una libreria di funzioni (thread
  libr...
Libreria Pthreads
• Standard ANSI/IEEE POSIX 1003.1
  – Gestione thread: creazione, distruzione,
    sincronizzazione
  – ...
Creazione thread
• Signature: int pthread_create(
  pthread_t *thread, pthread_attr_t *attr,
  void *(*start_routine)(void...
Creazione thread
• Signature: int pthread_create(
  pthread_t *thread, pthread_attr_t *attr,
  void *(*start_routine)(void...
Creazione thread
• Un thread, a sua volta, può creare altri thread con
  la pthread_create()
• Non esiste gerarchia o dipe...
Terminazione thread
• Le cause di terminazione di un thread sono
  molteplici
  – Il thread ritorna dalla funzione
  – Il ...
Terminazione thread
• Signature: void pthread_exit(void *retval);
  – Descrizione: termina l'esecuzione del thread
    inv...
Attributi dei thread
• Ad un thread è associata una struttura dati
  identificante le sue proprietà (attributi):
  pthread...
Attributi dei thread
• Signature: int pthread_attr_init(pthread_attr_t
  *attr);
  – Descrizione: inizializza una struttur...
Attributi dei thread
• Signature: int pthread_attr_destroy
  (pthread_attr_t *attr);
  – Descrizione: resetta una struttur...
Attributi dei thread
• Signature: int pthread_set_detachstate
  (pthread_attr_t *attr, int detachstate);
  – Descrizione: ...
Attributi dei thread
• Signature: int pthread_set_detachstate
  (pthread_attr_t *attr, int detachstate);
  – Descrizione: ...
Sincronizzazione thread
• La sincronizzazione fra thread creante e thread
  avviene in maniera simile ai processi
• Si uti...
Sincronizzazione thread
• Come si specifica la proprietà “joinable” sui
  thread?
• Step 1: si dichiara una variabile “att...
Sincronizzazione thread




                          42
Sincronizzazione thread
• Signature: int pthread_join (pthread_t th, void
  **thread_return);
  – Descrizione:
     ♦ sosp...
Concorrenza thread
• Per gestire l'accesso concorrente a variabili
  condivise, si utilizza il costrutto dei semafori
  – ...
Concorrenza thread
• Scenario tipico di utilizzo dei mutex
  – Creazione ed inizializzazione di una variabile di
    tipo ...
Concorrenza thread
• Dichiarazione mutex: può avvenire in due modi
  distinti
  – Dichiarazione statica:
    pthread_mutex...
Concorrenza thread
• Signature: int pthread_mutex_init
  (pthread_mutex_t *mutex, const
  pthread_mutex_attr_t *mutexattr)...
Concorrenza thread
• Signature: int pthread_mutex_destroy
  (pthread_mutex_t *mutex);
  – Descrizione:
     ♦ Resetta lo s...
Concorrenza thread
• Signature: int pthread_mutex_lock
  (pthread_mutex_t *mutex);
  – Descrizione:
     ♦ Prova ad acquis...
Concorrenza thread
• Signature: int pthread_mutex_trylock
  (pthread_mutex_t *mutex);
  – Descrizione:
     ♦ Provaad acqu...
Concorrenza thread
• Signature: int pthread_mutex_unlock
  (pthread_mutex_t *mutex);
  – Descrizione:
     ♦ Rilascia
    ...
Variabili condizione
• Le variabili condizione costituiscono un
  ulteriore meccanismo per la sincronizzazione
  fra threa...
Variabili condizione
• Scenario tipico di utilizzo - variabili condizione
• Tre thread:
  – Thread Main: thread di control...
Variabili condizione
• Thread Main:
  – Dichiara ed inizializza (strutture) dati globali che
    necessitano di sincronizz...
Variabili condizione
• Thread A:
  – Lavora fino a quando non si deve interrompere,
    in attesa che una condizione si ve...
Variabili condizione
• Thread B:
  – Lavora
  – Acquisisce il lock sul mutex
  – Cambia il valore della variabile su cui è...
Variabili condizione
• Rappresentate dal tipo di dato opaco
  pthread_cond_t
• Devono essere inizializzate prima di poter
...
Variabili condizione
• Signature: int pthread_cond_init
  (pthread_cond_t *cond, const
  pthread_condattr_t *cond_attr);
 ...
Variabili condizione
• Signature: int pthread_cond_destroy
  (pthread_cond_t *cond);
  – Descrizione:
     ♦ Resetta una v...
Variabili condizione
• Signature: int pthread_condattr_init
  (pthread_condattr_t *attr);
  – Descrizione: inizializza una...
Variabili condizione
• Signature: int pthread_condattr_init
  (pthread_condattr_t *attr);
  – Descrizione: inizializza una...
Variabili condizione
• Signature: int pthread_condattr_destroy
  (pthread_condattr_t *attr);
  – Descrizione: resetta una ...
Variabili condizione
• Signature: int pthread_cond_wait
  (pthread_cond_t *cond, pthread_mutex_t
  *mutex);
  – Descrizion...
Variabili condizione
• Signature: int pthread_cond_wait
  (pthread_cond_t *cond, pthread_mutex_t
  *mutex);
  – Ingresso:
...
Variabili condizione
• Signature: int pthread_cond_signal
  (pthread_cond_t *cond);
  – Descrizione:
     ♦ Segnala (risve...
Variabili condizione
• Signature: int pthread_cond_broadcast
  (pthread_cond_t *cond);
  – Descrizione:
     ♦ Segnala (ri...
Upcoming SlideShare
Loading in …5
×

Sistemi Operativi: Thread - Lezione 08

2,779 views
2,690 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,779
On SlideShare
0
From Embeds
0
Number of Embeds
22
Actions
Shares
0
Downloads
57
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Sistemi Operativi: Thread - Lezione 08

  1. 1. Svantaggi legati all'uso di processi • La gestione tradizionale dei processi può diventare molto onerosa dal punto di vista computazionale – Creazione: allocazione dello spazio di indirizzamento, e successiva popolazione – Context switch: salvataggio e ripristino degli spazi di indirizzamento (codice, dati, stack) di due processi • Con una applicazione con molti processi (server), il SO rischia di passare la maggior parte del tempo a svolgere operazioni interne di gestione, piuttosto che ad eseguire codice applicativo 1
  2. 2. Svantaggi legati all'uso di processi • La nozione classica di processo ingloba due concetti ben distinti, gestiti dal SO: – Gestione delle risorse: ciascun processo ha assegnati un proprio spazio di indirizzamento (solitamente, indipendente) ed alcune risorse – Esecuzione traccia: ciascun processo esegue porzioni di codice sequenzialmente e concorrentemente con altri processi • La gestione dell'esecuzione di una traccia non comporta particolari aggravi computazionali, essendo direttamente gestita dal processore • La lentezza della gestione risiede nella complessità della gestione delle risorse del processo 2
  3. 3. Separazione risorse-esecuzione • Nei SO moderni, i due concetti ora visti possono essere disaccoppiati – L'unità base di esecuzione di una traccia prende il nome di thread – L'unità base proprietaria delle risorse prende il nome di processo • Ciascun processo può essere visto come: – un contenitore di risorse – un contenitore di codice – Uno strumento per far partire tracce sequenziali (“fili” di codice, thread) di esecuzione del codice 3
  4. 4. Thread • Un thread è l'insieme dei dati utilizzati per eseguire, in maniera concorrente, più tracce relative al codice di un processo • Tutti i thread generati da un processo ne condividono il codice e le risorse • In tal modo, le operazioni di gestione non toccano praticamente più lo spazio di indirizzamento! – L'aggravio di gestione si riduce sensibilmente • Un SO in grado di gestire i thread è detto multithreaded • Tutti i SO moderni supportano il multithreading 4
  5. 5. Ambiente a processi (single threaded) • Ciascun processo processo file contiene tutte le PCB informazioni per dati poterlo eseguire • Ambiente single stack IP threaded: ciascun processo può codice eseguire al più una traccia traccia in • Informazioni: esecuzione – Spazio indirizzi – Risorse prenotate – Registri interni 5
  6. 6. Ambiente multithreaded • Ciascun processo processo file contiene tutte le PCB informazioni per dati poterlo eseguire • Ambiente codice IP multithreaded: ogni thread esegue TCB TCB TCB tracce di codice • TCB: Thread Control IP stack IP stack IP stack Block (simile a PCB) – Registri, Stack traccia in traccia in traccia in esecuzione esecuzione esecuzione – Variabili “locali” – Stato esecuzione 6
  7. 7. Processi vs. Thread Processo Thread 7
  8. 8. Benefici del multithreading • Reattività – Il blocco di un thread non inficia gli altri – Maggiore fluidità di esecuzione • Condivisione risorse – I thread condividono la memoria e le risorse del processo che li ha generati • Economia – Alloco e distruggo risorse di processo una volta • Architetture SMP – I thread eseguono in parallelo sui diversi processori – Il singolo processo non li può sfruttare 8
  9. 9. Problemi del multithreading • Modello di programmazione complesso – La condivisione delle risorse e della memoria implica spesso la gestione della concorrenza degli accessi a queste ultime – Ulteriori passi di programmazione, spesso complessi • Debugging complesso – E' molto difficile riprodurre bug in presenza di accessi concorrenti alle risorse – E' spesso difficile individuare le cause del bug 9
  10. 10. Applicazioni del multithreading • Quali applicazioni si lasciano implementare tramite i thread? Tutte quelle applicazioni: – che possono essere spezzate in più parti da eseguire concorrentemente/parallelamente – in cui alcune parti si bloccano spesso per fare I/ O, mentre altre no – che devono gestire eventi asincroni (bottone STOP del browser) 10
  11. 11. Supporto del SO ai thread • Il SO deve fornire un supporto per la gestione dei thread – Supporto allo user level, tramite librerie di funzioni (API) per gestire n thread in esecuzione – Supporto al kernel level, tramite una astrazione di traccia di esecuzione • Le diverse implementazioni di supporto ai thread svariano da un estremo all'altro – Implementazioni puramente a livello applicativo – Implementazioni puramente a livello kernel – Implementazioni “miste” • Adozione di molteplici modelli di multithreading 11
  12. 12. Modello Many-to-One (N:1) • A più supporti user level corrisponde un solo supporto kernel level – Il kernel vede una sola traccia di esecuzione, ossia solo un processo che esegue – Il processo usa delle funzionalità applicative per simulare uno scheduler di mini-processi – I mini-processi non sono noti al kernel del SO – Ciascun mini-processo è, in realtà, una astrazione per eseguire una traccia di una funzione del processo • Implementazioni: Green threads (Solaris), GNU Portable Threads, Java threads, Ruby threads 12
  13. 13. Modello Many-to-One (N:1) Create, stop, schedule, Processo resume, destroy Main Thread library User space Thread Thread Thread PCB Kernel space 13
  14. 14. Modello Many-to-One (N:1) • Vantaggi – Gestione efficientissima dei thread (non viene coinvolto lo scheduler del kernel) – Non richiede un kernel multithreaded per poter essere implementato • Svantaggi – Se un thread effettua una chiamata bloccante, il processo si blocca, e con esso tutti i thread – I thread sono legati allo stesso processo, e non possono eseguire su processori fisici distinti 14
  15. 15. Modello One-to-One (1:1) • Ad un supporto user level corrisponde un supporto kernel level – Il kernel vede una traccia di esecuzione distinta per thread; ho l'analogo di un PCB per ciascun thread in esecuzione – Il processo usa una system call simile a fork() per invocare il meccanismo di creazione – I thread vengono schedulati dallo scheduler del kernel, come tutti gli altri processi • Implementazioni: GNU/Linux (LinuxThreads, NPTL), Windows 95/98/2000/XP/NT 15
  16. 16. Modello One-to-One (1:1) Processo Thread Thread Thread Thread Main function function User space Create, f destroy Kernel PCB TCB TCB space 16
  17. 17. Modello One-to-One (1:1) • Vantaggi – Se un thread effettua una chiamata bloccante, non blocca gli altri thread – I thread sono rappresentati da altrettanti TCB, e possono essere eseguiti su processori fisici distinti • Svantaggi – Gestione meno efficiente dei thread (usa lo scheduler del kernel) – Richiede un kernel multithreaded per poter essere implementato 17
  18. 18. Modello Many-to-Many (N:M) • Ad N supporti user level corrispondono M supporti kernel level (N>M) – Unione del modelli 1:1 ed N:1; intende prendere il meglio dei due modelli – Il processo usa una system call simile a fork() per creare M thread kernel level – Ciascuno di questi M kernel level thread crea N/ M thread user level • Implementazioni: IRIX, HP-UX, Tru64 UNIX, Solaris (fino a V.9) 18
  19. 19. Modello Many-to-Many (N:M) ProcessoCreate, stop, sched, ProcessoCreate, stop, sched, resume, destroy resume, destroy Main Main Thread Thread library library User space Thread Thread Thread Thread Thread Thread Kernel PCB TCB space Create, destroy 19
  20. 20. Quando si applicano i modelli? • Modello Many-to-One – Buono per applicazioni di tipo parallelo con pochissimo I/O – Lo scheduler del kernel non le rallenta – Thread non schedulabili su più processori • Modello One-to-One – Ideale per applicazioni di tipo parallelo o distribuito con tanto I/O (server) – Mantiene elevato il grado di concorrenza – Ha soppiantato tutti gli altri modelli • Modello Many-to-Many – Concepito per applicazioni suddivise in una parte I/O (1:1) ed in una parte di calcolo (M:1) 20
  21. 21. Multithreading: creazione/immagine • In un programma multithreaded la semantica delle operazioni fork() ed exec() cambia • Se un thread invoca la chiamata fork(): – può duplicare se stesso – può duplicare l'intero gruppo di thread • Se un thread invoca la chiamata exec(): – Sovrascrive l'immagine di tutti i thread e del processo invocante • Solitamente, si evita l'uso della exec() in ambiente multithreaded – Si associano i thread a funzioni che devono essere eseguite 21
  22. 22. Multithreading: cancellazione • La cancellazione è l'operazione di terminazione prematura di un thread • Il thread selezionato per una cancellazione è chiamato thread bersaglio (target thread) • La cancellazione del target bersaglio può avvenire in due modalità distinte: – cancellazione asincrona: un altro thread uccide direttamente il thread bersaglio – cancellazione differita: il thread bersaglio controlla periodicamente (cancellation point) se deve terminare, in modo da uscire in maniera pulita 22
  23. 23. Multithreading: gestione segnali • In ambiente single threaded, il segnale viene inviato ad un processo • In ambiente multithreaded, a quale thread va inviato il segnale? – al thread cui il segnale si riferisce – a ciascun thread (CTRL-C) – a specifici thread ♦ il primo thread che non blocca il segnale ♦ Identificato dal TID – ad un thread speciale, di controllo (Solaris 2) 23
  24. 24. Multithreading: thread pool • All'aumentare del numero di flussi concorrenti, le risorse del sistema possono esaurire rapidamente • Per impedire l'esaurimento delle risorse, un software multithreaded pre-crea un insieme di thread (thread pool) – Quando viene lanciato un thread, lo si preleva dal gruppo – Quando un thread termina, lo si restituisce al gruppo – Le operazioni di creazione/distruzione sono molto più veloci – Si limita il numero di thread in esecuzione 24
  25. 25. Multithreading: Thread Specific Data • In particolari circostanze, ciascun thread può necessitare di una copia privata di alcuni dati, detti dati specifici (thread locale) – Variabili “globali” per thread – Variabili “static” per thread • La maggior parte delle librerie pthread forniscono il supporto per i thread locale 25
  26. 26. Modelli di programmazione • L'uso dei thread permette l'implementazione efficiente di alcuni modelli di programmazione • Modello Pipeline: – I thread eseguono “a catena”, uno dopo l'altro • Modello Master-Slave: – Il thread master coordina l'esecuzione dei thread slave (che effettuano il lavoro vero e proprio) • Modello Worker: – Tutti i thread lavorano 26
  27. 27. Modelli di programmazione Pipeline M Master-Slave S S S W W Worker W W 27
  28. 28. Librerie di threading • Ciascuna implementazione del supporto ai thread fornisce una libreria di funzioni (thread library) per la gestione dei thread – La libreria può fare uso nullo, parziale o completo delle funzionalità offerte dal kernel • Implementazioni: – Pthreads: standard POSIX, kernel level – Win32: kernel level – Java: user level 28
  29. 29. Libreria Pthreads • Standard ANSI/IEEE POSIX 1003.1 – Gestione thread: creazione, distruzione, sincronizzazione – Gestione concorrenza: meccanismo dei mutex – Comunicazione concorrente: meccanismo per creare, distruggere e segnalare thread sulla base dei valori di specifiche variabili condizione (condition variables) • Più di 60 funzioni per gestire thread come tipi di dato opachi (pthread_t) • Concepita per i linguaggi C, C++ • Compilazione: gcc -pthread – include <pthread.h> 29
  30. 30. Creazione thread • Signature: int pthread_create( pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); – Descrizione: crea un nuovo thread, eseguente la funzione start_routine con argomento arg – Ingresso: ♦ Un puntatore alla struttura thread identificante il thread da eseguire ♦ Un puntatore alla struttura “attributi” che imposta le caratteristiche del thread (NULL) ♦ Un puntatore alla funzione start_routine ♦ Un puntatore all'argomento arg 30
  31. 31. Creazione thread • Signature: int pthread_create( pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); – Descrizione: crea un nuovo thread, eseguente la funzione start_routine con argomento arg – Ritorno: ♦ 0 -> tutto ok – Il TID del thread appena creato è salvato all'interno della struttura dati thread ♦ !=0 -> errore – tipicamente, le risorse del sistema sono insufficienti per la creazione di un thread 31
  32. 32. Creazione thread • Un thread, a sua volta, può creare altri thread con la pthread_create() • Non esiste gerarchia o dipendenza fra I vari thread 32
  33. 33. Terminazione thread • Le cause di terminazione di un thread sono molteplici – Il thread ritorna dalla funzione – Il thread invoca una chiamata di funzione pthread_exit() – Il thread è cancellato da un altro thread tramite la funzione pthread_cancel() – Il processo invocante carica un'altra immagine con la funzione exec() – Il processo invocante esce tramite la funziona exit() 33
  34. 34. Terminazione thread • Signature: void pthread_exit(void *retval); – Descrizione: termina l'esecuzione del thread invocante – Ingresso: ♦ un puntatore ad una variabile in cui sarà scritto il codice di uscita del thread (potrà essere consultato tramite la pthread_join()) – Ritorno: nessuno 34
  35. 35. Attributi dei thread • Ad un thread è associata una struttura dati identificante le sue proprietà (attributi): pthread_attr_t • Tale struttura contiene una maschera di bit, che rappresenta le proprietà attivate • Alcune proprietà: – Thread “joinabile” – Algoritmo di scheduling utilizzato – Scope delle variabili 35
  36. 36. Attributi dei thread • Signature: int pthread_attr_init(pthread_attr_t *attr); – Descrizione: inizializza una struttura pthread_attr_t – Ingresso: ♦ un puntatore ad una struttura pthread_attr_t – Ritorno: ♦ Sempre 0 36
  37. 37. Attributi dei thread • Signature: int pthread_attr_destroy (pthread_attr_t *attr); – Descrizione: resetta una struttura pthread_attr_t e rilascia le risorse allocate – Ingresso: ♦ un puntatore ad una struttura pthread_attr_t – Ritorno: ♦ Sempre 0 37
  38. 38. Attributi dei thread • Signature: int pthread_set_detachstate (pthread_attr_t *attr, int detachstate); – Descrizione: imposta il flag “detached” del thread con proprietà attr al valore detachstate – Ingresso: ♦ Un puntatore ad una struttura pthread_attr_t ♦ Lo stato desiderato (PTHREAD_CREATE_JOINABLE) – Ritorno: ♦ 0: -> tutto OK ♦ !=0: un errore 38
  39. 39. Attributi dei thread • Signature: int pthread_set_detachstate (pthread_attr_t *attr, int detachstate); – Descrizione: imposta il flag “detached” del thread con proprietà attr al valore detachstate – Ingresso: ♦ Un puntatore ad una struttura pthread_attr_t ♦ Lo stato desiderato (PTHREAD_CREATE_JOINABLE) – Ritorno: ♦ 0: -> tutto OK ♦ !=0: un errore 39
  40. 40. Sincronizzazione thread • La sincronizzazione fra thread creante e thread avviene in maniera simile ai processi • Si utilizza la funzione pthread_join() (analogo della waitpid()) • Il thread invocante (o il processo) si blocca fino a quando un thread specificato da threadid termina • Il thread invocante (o il processo) può leggere il codice di uscita • A differenza dei processi, bisogna specificare esplicitamente che un thread sia “joinabile” 40
  41. 41. Sincronizzazione thread • Come si specifica la proprietà “joinable” sui thread? • Step 1: si dichiara una variabile “attributi” di tipo pthread_attr_t • Step 2: si inizializza la variabile con la funzione pthread_attr_init() • Step 3: si imposta l'attributo “detached” con la funzione pthread_attr_setdetachstate() • Step 4: in un punto successivo, si rilasciano le risorse allocate con la funzione pthread_attr_destroy() 41
  42. 42. Sincronizzazione thread 42
  43. 43. Sincronizzazione thread • Signature: int pthread_join (pthread_t th, void **thread_return); – Descrizione: ♦ sospende l'esecuzione del thread invocante fino all'uscita del thread identificato da th ♦ Se thread_return != NULL, scrive il codice di uscita nella cella puntata da thread_return – Ingresso: ♦ un puntatore ad una struttura pthread_attr_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: un errore 43
  44. 44. Concorrenza thread • Per gestire l'accesso concorrente a variabili condivise, si utilizza il costrutto dei semafori – Variabili mutex (MUTual Exclusion): interi posti a 0 (risorsa libera) o 1 (risorsa occupata) pthread_mutex_t – Ciascun mutex agisce come un lucchetto (lock) sugli altri thread che vogliono accedere alla risorsa – Due stati: ♦ LOCKED: risorsa prenotata da un thread ♦ UNLOCKED: risorsa libera – Impedisce le cosiddette corse critiche (accessi simultanei a variabili condivise, con indeterminatezza del risultato finale) 44
  45. 45. Concorrenza thread • Scenario tipico di utilizzo dei mutex – Creazione ed inizializzazione di una variabile di tipo mutex – Più thread provano a prenotare la risorsa, cercando di ottenere il lock sul mutex – Un solo thread ci riesce (quello che arriva per primo) e prenota la risorsa per se – Il thread modifica la risorsa in questione – Il thread rilascia la risorsa – Un altro thread acquisisce il lock sul mutex – Il processo si ripete – Il mutex viene distrutto 45
  46. 46. Concorrenza thread • Dichiarazione mutex: può avvenire in due modi distinti – Dichiarazione statica: pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; – Dichiarazione dinamica: pthread_mutex_init() 46
  47. 47. Concorrenza thread • Signature: int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr); – Descrizione: ♦ Inizializza il mutex puntato da mutex con gli attributi puntati di mutexattr ♦ Il mutex è UNLOCKED (risorsa libera) – Ingresso: ♦ un puntatore ad una struttura pthread_mutex_t ♦ un puntatore ad una struttura pthread_mutex_attr_t – Ritorno: ♦ Sempre 0 47
  48. 48. Concorrenza thread • Signature: int pthread_mutex_destroy (pthread_mutex_t *mutex); – Descrizione: ♦ Resetta lo stato del mutex puntato da mutex – Ingresso: ♦ un puntatore ad una struttura pthread_mutex_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: il mutex è attualmente bloccato 48
  49. 49. Concorrenza thread • Signature: int pthread_mutex_lock (pthread_mutex_t *mutex); – Descrizione: ♦ Prova ad acquisire un lock su mutex ♦ Se mutex=UNLOCKED, lo acquisisce e torna ♦ Se mutex=LOCKED, si blocca fino a quando la risorsa non si libera – Ingresso: ♦ un puntatore ad una struttura pthread_mutex_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: mutex non inizializzato o già bloccato dallo stesso thread 49
  50. 50. Concorrenza thread • Signature: int pthread_mutex_trylock (pthread_mutex_t *mutex); – Descrizione: ♦ Provaad acquisire un lock su mutex ♦ Se mutex=UNLOCKED, lo acquisisce e torna ♦ Se mutex=LOCKED, ritorna e non si blocca – Ingresso: ♦ un puntatore ad una struttura pthread_mutex_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: mutex non inizializzato, già bloccato dallo stesso thread, già bloccato da un altro thread 50
  51. 51. Concorrenza thread • Signature: int pthread_mutex_unlock (pthread_mutex_t *mutex); – Descrizione: ♦ Rilascia la risorsa associata al mutex ♦ Reimposta il mutex ad UNLOCKED – Ingresso: ♦ un puntatore ad una struttura pthread_mutex_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: mutex non inizializzato, già bloccato dallo stesso thread, già bloccato da un altro thread 51
  52. 52. Variabili condizione • Le variabili condizione costituiscono un ulteriore meccanismo per la sincronizzazione fra thread • Idea: associare eventi al valore di alcune variabili – Una variabile assume un valore->scatta un evento – Un'altra variabile, detta variabile condizione, viene usata per segnalare l'evento – Callback: gestione asincrona di eventi tramite invocazione di funzioni • Senza le variabili di condizione, un thread deve continuamente controllare (poll) l'arrivo di eventi, sprecando inutilmente risorse di calcolo 52
  53. 53. Variabili condizione • Scenario tipico di utilizzo - variabili condizione • Tre thread: – Thread Main: thread di controllo che schedula i thread di lavoro A e B – Thread A: lavora fino ad un certo punto, poi si ferma in attesa di un evento – Thread B: lavora fino ad un certo punto, poi scatena l'evento sul quale sta aspettando A • Che funzioni eseguono i tre thread? 53
  54. 54. Variabili condizione • Thread Main: – Dichiara ed inizializza (strutture) dati globali che necessitano di sincronizzazione – Dichiara ed inizializza un oggetto di tipo “variabile condizione” – Dichiara ed inizializza un mutex associato alla variabile condizione – Crea i thread A e B – Join dei thread A e B 54
  55. 55. Variabili condizione • Thread A: – Lavora fino a quando non si deve interrompere, in attesa che una condizione si verifichi – Acquisisce il lock sul mutex – Si blocca (pthread_cond_wait()) fino a quando non si verifica l'evento, segnalato da un altro thread (Thread B) – pthread_cond_wait() sblocca automaticamente il mutex, che può essere utilizzato dall'altro thread per acquisire uso esclusivo sulla variabile che rappresenta l'evento – Quando arriva il segnale, Thread A si sveglia ed il mutex viene di nuovo assegnato a lui – Thread A rilascia esplicitamente il mutex 55
  56. 56. Variabili condizione • Thread B: – Lavora – Acquisisce il lock sul mutex – Cambia il valore della variabile su cui è bloccato Thread A – Se il valore della variabile è tale da scatenare un evento, segnala Thread A tramite una variabile condizione – Rilascia esplicitamente il mutex 56
  57. 57. Variabili condizione • Rappresentate dal tipo di dato opaco pthread_cond_t • Devono essere inizializzate prima di poter essere utilizzate • Possono specificare dei propri attributi • L'inizializzazione avviene in due modi distinti: – Inizializzazione statica: pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER; – Inizializzazione dinamica: pthread_cond_init() 57
  58. 58. Variabili condizione • Signature: int pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *cond_attr); – Descrizione: ♦ Inizializza la variabile condizione cond utilizzando gli attributi cond_attr (NULL per default) – Ingresso: ♦ un puntatore ad una struttura pthread_cond_t ♦ un puntatore ad una struttura pthread_condattr_t – Ritorno: ♦ Sempre 0 58
  59. 59. Variabili condizione • Signature: int pthread_cond_destroy (pthread_cond_t *cond); – Descrizione: ♦ Resetta una variabile condizione cond, rilasciando le risorse che potrebbe avere allocato – Ingresso: ♦ un puntatore ad una struttura pthread_cond_t – Ritorno: ♦ 0: -> tutto OK ♦ !=0: la variabile condizione è attualmente utilizzata da un qualche thread 59
  60. 60. Variabili condizione • Signature: int pthread_condattr_init (pthread_condattr_t *attr); – Descrizione: inizializza una struttura pthread_condattr_t – Ingresso: ♦ un puntatore ad una struttura pthread_condattr_t – Ritorno: ♦ Sempre 0 60
  61. 61. Variabili condizione • Signature: int pthread_condattr_init (pthread_condattr_t *attr); – Descrizione: inizializza una struttura pthread_condattr_t – Ingresso: ♦ un puntatore ad una struttura pthread_condattr_t – Ritorno: ♦ Sempre 0 61
  62. 62. Variabili condizione • Signature: int pthread_condattr_destroy (pthread_condattr_t *attr); – Descrizione: resetta una struttura pthread_condattr_t – Ingresso: ♦ un puntatore ad una struttura pthread_condattr_t – Ritorno: ♦ Sempre 0 62
  63. 63. Variabili condizione • Signature: int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex); – Descrizione: ♦ Blocca il thread invocante fino a quando non viene segnalata la condizione cond ♦ Deve essere invocata con mutex bloccato (altrimenti un thread potrebbe segnalare la condizione prima che un altro possa ascoltarla); il mutex viene automaticamente sbloccato al termine della chiamata ♦ Quando si verifica la condizione, il thread viene svegliato e mutex viene acquisito di nuovo (ricordatevi di rilasciarlo!) 63
  64. 64. Variabili condizione • Signature: int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex); – Ingresso: ♦ Un puntatore alla struttura pthread_cont_t che rappresenta la variabile condizione su cui aspettare ♦ Un puntatore alla struttura pthread_mutex_t che rappresenta il mutex associato alla variabile condizione – Ritorno: ♦ Sempre 0 64
  65. 65. Variabili condizione • Signature: int pthread_cond_signal (pthread_cond_t *cond); – Descrizione: ♦ Segnala (risveglia) un altro thread in seguito al verificarsi di una data condizione ♦ Più thread possono aspettare una data condizione; pthread_cond_signal() ne risveglia solamente uno – Ingresso: ♦ Un puntatore alla struttura pthread_cont_t che rappresenta la variabile condizione su cui aspettare – Ritorno: ♦ Sempre 0 65
  66. 66. Variabili condizione • Signature: int pthread_cond_broadcast (pthread_cond_t *cond); – Descrizione: ♦ Segnala (risveglia) un insieme di thread in seguito al verificarsi di una data condizione ♦ Più thread possono aspettare una data condizione; pthread_cond_broadcast() li risveglia tutti – Ingresso: ♦ Un puntatore alla struttura pthread_cont_t che rappresenta la variabile condizione su cui aspettare – Ritorno: ♦ Sempre 0 66

×