(fork, wait, ...) , Thread (pthread) - lucidi - Dipartimento di Ingegneria ...
(fork, wait, ...) , Thread (pthread) - lucidi - Dipartimento di Ingegneria ...
(fork, wait, ...) , Thread (pthread) - lucidi - Dipartimento di Ingegneria ...
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Sistemi Operativi<br />
Corso <strong>di</strong> Laurea Triennale<br />
in <strong>Ingegneria</strong> Informatica<br />
Esercitazione 6<br />
• Programmazione concorrente<br />
• Ripasso processi<br />
• <strong>Thread</strong>:<br />
• Join, mutex, con<strong>di</strong>tion<br />
daniel.cesarini@for.unipi.it<br />
1
Domande sulle<br />
lezioni passate
Sommario<br />
Programmazione concorrente<br />
Processi<br />
Operazioni sui processi<br />
Creazione, comunicazione, ...<br />
<strong>Thread</strong> POSIX<br />
Operazioni sui <strong>Thread</strong><br />
Creazione, terminazione, join<br />
Sincronizzazione<br />
Semafori<br />
Variabili con<strong>di</strong>tion<br />
Aspetti preliminari dei thread POSIX 3
Processi Unix
System call per i processi<br />
Creazione <strong>di</strong> processi: <strong>fork</strong>()<br />
Terminazione: exit()<br />
Sospensione in attesa della<br />
terminazione <strong>di</strong> figli: <strong>wait</strong>()<br />
Sostituzione <strong>di</strong> co<strong>di</strong>ce e dati: exec..()<br />
Aspetti preliminari dei thread POSIX 5
Creazione <strong>di</strong> processi: <strong>fork</strong>() - 1<br />
<strong>fork</strong>() consente a un processo <strong>di</strong><br />
generare un processo figlio:<br />
Padre e figlio con<strong>di</strong>vidono lo stesso co<strong>di</strong>ce<br />
Il figlio ere<strong>di</strong>ta una copia dei dati (<strong>di</strong> utente<br />
e <strong>di</strong> kernel) del padre<br />
padre<br />
<strong>fork</strong>( )<br />
figlio<br />
Aspetti preliminari dei thread POSIX 6
Relazione padre-figlio - <strong>fork</strong>()<br />
Concorrenza: padre e figlio procedono in<br />
parallelo<br />
Il co<strong>di</strong>ce è con<strong>di</strong>viso<br />
Lo spazio degli in<strong>di</strong>rizzi è duplicato:<br />
Ogni variabile del figlio è inizializzata con il valore<br />
assegnatole dal padre prima della <strong>fork</strong>()<br />
La user structure è duplicata:<br />
Le risorse allocate al padre prima della generazione sono<br />
con<strong>di</strong>vise col figlio<br />
Stessa gestione dei segnali per padre e figlio<br />
Il figlio nasce con lo stesso Program Counter del padre: la<br />
prima istruzione eseguita è quella che segue<br />
imme<strong>di</strong>atamente la <strong>fork</strong>()<br />
Aspetti preliminari dei thread POSIX 7
Esempio <strong>di</strong> <strong>fork</strong>() ed execl()<br />
#include <br />
#include <br />
#include <br />
int int main main (int (int argc, char char * argv[]) {<br />
int int pid, pid, status;<br />
pid=<strong>fork</strong>();<br />
if if (pid==0) {<br />
execl("/bin/ls", "ls", "-l", (char *) *) 0); 0);<br />
printf("exec fallita!\n");<br />
exit(1);<br />
}<br />
else else if if (pid>0) {<br />
pid=<strong>wait</strong>(&status);<br />
/* /* Gestione dello stato */ */<br />
}<br />
else else printf("<strong>fork</strong> fallita!\n");<br />
}<br />
Aspetti preliminari dei thread POSIX 8
Co<strong>di</strong>ce nella cartella esempi<br />
<strong>fork</strong>1.c<br />
Called once, returns twice<br />
<strong>fork</strong>2.c<br />
Uso <strong>di</strong> getpid() e getppid()<br />
exec1.c<br />
Uso <strong>di</strong> execv (path ...)<br />
fdtest1.c<br />
Read e Write dagli stessi file<br />
Aspetti preliminari dei thread POSIX 9
Co<strong>di</strong>ce nella cartella esempi<br />
<strong>di</strong>e1.c<br />
Padre termina prima del figlio ( ./<strong>di</strong>e)<br />
<strong>di</strong>e2.c<br />
Figlio termina prima del padre ( ./<strong>di</strong>e2 &)<br />
<strong>di</strong>e3.c<br />
Signal handler in azione ( ./<strong>di</strong>e3 &)<br />
Aspetti preliminari dei thread POSIX 10
<strong>Thread</strong> POSIX<br />
thread = filo
<strong>Thread</strong> POSIX<br />
Introduzione ai thread POSIX<br />
operazioni elementari sui thread<br />
Sincronizzazione<br />
Semafori<br />
semafori <strong>di</strong> mutua esclusione<br />
semafori generali<br />
utilizzo ed esempi<br />
Variabili con<strong>di</strong>tion<br />
generalità<br />
utilizzo ed esempi<br />
Aspetti preliminari dei thread POSIX 12
<strong>Thread</strong> POSIX: aspetti<br />
preliminari
Processo vs <strong>Thread</strong> (2 <strong>di</strong> 2)<br />
I thread eseguono su memoria con<strong>di</strong>visa nell’ambito dello stesso processo,<br />
quando un thread viene creato con<strong>di</strong>vide il suo spazio <strong>di</strong> memoria con gli<br />
altri thread che fanno parte del processo<br />
Sono anche chiamati lightweight process o processi leggeri perché<br />
possiedono un contesto più snello rispetto ai processi<br />
Aspetti preliminari dei thread POSIX 14
<strong>Thread</strong><br />
<strong>Thread</strong><br />
è l’unità granulare in cui un processo può essere<br />
sud<strong>di</strong>viso e che può essere eseguito in parallelo<br />
ad altri thread<br />
è parte del processo che viene eseguita in<br />
maniera concorrente ed in<strong>di</strong>pendente<br />
internamente al processo stesso<br />
insieme <strong>di</strong> istruzioni che vengono eseguite in<br />
modo in<strong>di</strong>pendente rispetto al main<br />
Stato <strong>di</strong> un thread<br />
stack<br />
registri<br />
proprietà <strong>di</strong> scheduling<br />
stato dei segnali<br />
dati privati<br />
Aspetti preliminari dei thread POSIX 15
Funzioni delle API per Pthread<br />
Le API per Pthread <strong>di</strong>stinguono le funzioni in<br />
3 gruppi:<br />
<strong>Thread</strong> management<br />
funzioni per creare, eliminare, attendere la fine dei<br />
<strong>pthread</strong><br />
Mutexes:<br />
funzioni per supportare un tipo <strong>di</strong> sincronizzazione<br />
semplice chiamata “mutex” (mutua esclusione).<br />
funzioni per creare ed eliminare la struttura per la<br />
mutua esclusione <strong>di</strong> una risorsa, acquisire e rilasciare<br />
tale risorsa.<br />
Con<strong>di</strong>tion variables:<br />
funzioni a supporto <strong>di</strong> una sincronizzazione più<br />
complessa, <strong>di</strong>pendente dal valore <strong>di</strong> variabili, secondo<br />
i mo<strong>di</strong> definite dal programmatore.<br />
funzioni per creare ed eliminare la struttura per la<br />
sincronizzazione, per attendere e segnalare le<br />
mo<strong>di</strong>fiche delle variabili.<br />
Aspetti preliminari dei thread POSIX 16
Utilizzo<br />
includere l’header della libreria che<br />
contiene le definizioni dei <strong>pthread</strong><br />
#include <br />
Per interpretare correttamente i messaggi <strong>di</strong><br />
errore è necessario anche includere l’header<br />
<br />
compilare specificando la libreria<br />
gcc -l<strong>pthread</strong><br />
Libreria <strong>pthread</strong> (lib<strong>pthread</strong>) l<strong>pthread</strong><br />
Per ulteriori informazioni sulla compilazione fare<br />
riferimento alla documentazione della<br />
piattaforma utilizzata man <strong>pthread</strong> o man<br />
<strong>pthread</strong>s<br />
Aspetti preliminari dei thread POSIX 17
Convenzione sui nomi delle funzioni<br />
Gli identificatori della libreria dei Pthread iniziano<br />
con <strong>pthread</strong>_<br />
<strong>pthread</strong>_<br />
in<strong>di</strong>ca la gestione dei thread in generale<br />
<strong>pthread</strong>_attr_<br />
funzioni per gestire proprietà dei thread<br />
<strong>pthread</strong>_mutex_<br />
gestione della mutua esclusione<br />
<strong>pthread</strong>_mutexattr_<br />
proprietà delle strutture per la mutua esclusione<br />
<strong>pthread</strong>_cond_<br />
gestione delle variabili <strong>di</strong> con<strong>di</strong>zione<br />
<strong>pthread</strong>_condattr_<br />
proprietà delle variabili <strong>di</strong> con<strong>di</strong>zione<br />
<strong>pthread</strong>_key_<br />
dati speciali dei thread<br />
Aspetti preliminari dei thread POSIX 18
Gestione dei thread
Tipi definiti nella libreria <strong>pthread</strong><br />
All’interno <strong>di</strong> un programma un thread è<br />
rappresentato da un identificatore<br />
tipo opaco <strong>pthread</strong>_t<br />
Attributi <strong>di</strong> un thread<br />
tipo opaco <strong>pthread</strong>_attr_t<br />
tipo opaco: si definiscono così strutture ed altri oggetti usati da una libreria, la cui<br />
struttura interna non deve essere vista dal programma chiamante (da cui il<br />
nome) che li deve utilizzare solo attraverso dalle opportune funzioni <strong>di</strong> gestione.<br />
Aspetti preliminari dei thread POSIX 20
Identificatore del thread<br />
Processo: process id (pid)<br />
pid_t<br />
<strong>Thread</strong>: thread id (tid) <strong>pthread</strong>_t<br />
<strong>pthread</strong>_t <strong>pthread</strong>_self( void )<br />
restituisce il tid del thread chiamante<br />
Aspetti preliminari dei thread POSIX 21
Confronto tra thread<br />
int int <strong>pthread</strong>_equal( <strong>pthread</strong>_t t1, t1, <strong>pthread</strong>_t t2 t2 )<br />
confronta i due identificatori <strong>di</strong> thread.<br />
1 se i due identificatori sono uguali<br />
Aspetti preliminari dei thread POSIX 22
Creazione <strong>di</strong> un thread (1 <strong>di</strong> 2)<br />
int int <strong>pthread</strong>_create( <strong>pthread</strong>_t *thread,<br />
const <strong>pthread</strong>_attr_t *attr,<br />
void *(*start_routine)(void *), *),<br />
void *arg )<br />
crea una thread e lo rende eseguibile, cioè lo<br />
mette a <strong>di</strong>sposizione dello scheduler per<br />
farlo partire ().<br />
Non è preve<strong>di</strong>bile<br />
il momento dell'attivazione<br />
del thread<br />
Aspetti preliminari dei thread POSIX 23
Creazione <strong>di</strong> un thread (2 <strong>di</strong> 2)<br />
<br />
<br />
<strong>pthread</strong>_t *thread<br />
puntatore ad un identificatore <strong>di</strong> thread in cui verrà scritto<br />
l’identificatore univoco del thread creato (se creato con<br />
successo)<br />
const <strong>pthread</strong>_attr_t *attr<br />
attributi del processo da creare: può in<strong>di</strong>care le caratteristiche<br />
del thread riguardo alle operazioni <strong>di</strong> join o allo scheduling<br />
se NULL usa valori <strong>di</strong> default<br />
void *(*start_routine)(void *)<br />
è il nome (in<strong>di</strong>rizzo) della funzione da eseguire alla creazione del<br />
thread<br />
<br />
<br />
void *arg<br />
puntatore che viene passato come argomento a<br />
start_routine.<br />
Valore <strong>di</strong> ritorno<br />
0 in assenza <strong>di</strong> errore<br />
<strong>di</strong>verso da zero altrimenti<br />
attributi errati<br />
mancanza <strong>di</strong> risorse<br />
Aspetti preliminari dei thread POSIX 24
Terminazione <strong>di</strong> un thread<br />
void <strong>pthread</strong>_exit( void *value_ptr )<br />
<br />
<br />
<br />
<br />
Termina l’esecuzione del thread da cui viene chiamata<br />
Il sistema libera le risorse allocate al thread.<br />
Se il main termina prima che i thread da lui creati siano<br />
terminati e non chiama la funzione <strong>pthread</strong>_exit, allora<br />
tutti i thread sono terminati. Se invece il main chiama<br />
<strong>pthread</strong>_exit allora i thread possono continuare a vivere<br />
fino alla loro terminazione.<br />
void *value_ptr<br />
valore <strong>di</strong> ritorno del thread consultabile da altri thread<br />
attraverso la funzione <strong>pthread</strong>_join<br />
Aspetti preliminari dei thread POSIX 25
Esempio 1: creazione e terminazione<br />
(1 <strong>di</strong> 2)<br />
/* /* Include Include */ */<br />
#include #include <br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
#define #define NUM_THREADS NUM_THREADS 5<br />
/* /* Corpo Corpo del del thread thread */ */<br />
void void *PrintHello(void *PrintHello(void *num) *num) {<br />
printf("\n%d: printf("\n%d: Hello Hello World!\n", World!\n", num); num);<br />
<strong>pthread</strong>_exit(NULL);<br />
<strong>pthread</strong>_exit(NULL);<br />
}}<br />
Continua <br />
Aspetti preliminari dei thread POSIX 26
Esempio 1: creazione e terminazione<br />
(2 <strong>di</strong> 2)<br />
/* /* Programma Programma */ */<br />
int int main main (int (int argc, argc, char char *argv[]) *argv[])<br />
{<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threads[NUM_THREADS];<br />
threads[NUM_THREADS];<br />
int int rc, rc, t; t;<br />
for(t=0; for(t=0; t
Passaggio parametri (1 <strong>di</strong> 3)<br />
La <strong>pthread</strong>_create prevede un puntatore per il<br />
passaggio dei parametri al thread nel momento<br />
in cui comincia l’esecuzione.<br />
Si ponga attenzione nel caso il thread debba<br />
mo<strong>di</strong>ficare i parametri, oppure il chiamante<br />
debba mo<strong>di</strong>ficare i parametri, potrebbero<br />
insorgere problemi, meglio de<strong>di</strong>care una<br />
struttura dati ad hoc, per il passaggio.<br />
Aspetti preliminari dei thread POSIX 28
Passaggio parametri (2 <strong>di</strong> 3)<br />
Per riferimento con un cast a void*<br />
Esempio (errato)<br />
il ciclo mo<strong>di</strong>fica il contenuto dell’in<strong>di</strong>rizzo passato<br />
come parametro<br />
int<br />
int<br />
rc,<br />
rc,<br />
t;<br />
t;<br />
for(t=0; for(t=0; t
Passaggio parametri (3 <strong>di</strong> 3)<br />
Esempio (corretto)<br />
struttura dati univoca per ogni thread<br />
int int *taskids[NUM_THREADS];<br />
*taskids[NUM_THREADS];<br />
for(t=0; for(t=0; t
Esempio 2 errato: passaggio parametri (1<br />
<strong>di</strong> 2)<br />
/* /* Include Include */ */<br />
#include #include <br />
<br />
#include #include <br />
#include #include <br />
<br />
#define<br />
#define<br />
NUM_THREADS<br />
NUM_THREADS<br />
5<br />
/* /* Corpo Corpo del del thread thread */ */<br />
void void *PrintHello(void *PrintHello(void *num) *num) {<br />
printf("\n%d: printf("\n%d: Hello Hello World!\n", World!\n", *(int *(int *) *) num); num);<br />
<strong>pthread</strong>_exit(NULL);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Aspetti preliminari dei thread POSIX 31
Esempio 2 errato : passaggio parametri (2<br />
<strong>di</strong> 2)<br />
/* /* Programma Programma */ */<br />
int int main main (int (int argc, argc, char char *argv[]) *argv[])<br />
{<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threads[NUM_THREADS];<br />
threads[NUM_THREADS];<br />
int int rc, rc, t; t;<br />
}<br />
for(t=0; for(t=0; t
Esempio 2 : passaggio parametri (1<br />
<strong>di</strong> 2)<br />
/* /* Include Include */ */<br />
#include #include <br />
<br />
#include #include <br />
#include #include <br />
<br />
#define<br />
#define<br />
NUM_THREADS<br />
NUM_THREADS<br />
5<br />
/* /* Corpo Corpo del del thread thread */ */<br />
void void *PrintHello(void *PrintHello(void *num) *num) {<br />
printf("\n%d: printf("\n%d: Hello Hello World!\n", World!\n", *(int *(int *) *) num); num);<br />
<strong>pthread</strong>_exit(NULL);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Aspetti preliminari dei thread POSIX 33
Esempio 2 : passaggio parametri (2<br />
<strong>di</strong> 2)<br />
/* /* Programma Programma */ */<br />
int int main main (int (int argc, argc, char char *argv[]) *argv[])<br />
{<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threads[NUM_THREADS];<br />
threads[NUM_THREADS];<br />
int int rc, rc, t; t;<br />
int int *taskids[NUM_THREADS];<br />
*taskids[NUM_THREADS];<br />
for(t=0; for(t=0; t
Esercizi<br />
(parte uno)
Esercizio 1<br />
<strong>pthread</strong>s-1a-simple.c<br />
analizzare l'output<br />
cambiare <strong>pthread</strong>s_exit(NULL) in<br />
return(0)<br />
cosa succede?<br />
aggiungere il passaggio <strong>di</strong> un parametro ai<br />
thread passando a tutti lo stesso valore<br />
<strong>pthread</strong>s-1b-simple.c<br />
cosa cambia rispetto al precedente?<br />
<strong>pthread</strong>s-1c-simple.c<br />
soluzione dell'esercizio precedente<br />
Aspetti preliminari dei thread POSIX 36
<strong>pthread</strong>s-1a-simple.c<br />
#include <br />
#include <br />
#include <br />
#include <br />
#define NUM_THREADS 3<br />
void *thread_function(void* arg){<br />
printf("[<strong>Thread</strong>] Waiting for termination...\n");<br />
sleep(5);<br />
printf("[<strong>Thread</strong>] ...thread finished!\n");<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
int main(void){<br />
<strong>pthread</strong>_t tids[NUM_THREADS];<br />
int i, rc;<br />
printf("[Main] Starting...\n");<br />
for (i=0; i
<strong>pthread</strong>s-1b-simple.c (1 <strong>di</strong><br />
2)<br />
#include <br />
#include <br />
#include <br />
#include <br />
#define NUM_THREADS 3<br />
void *thread_function(void* arg){<br />
printf("[<strong>Thread</strong>] Waiting for termination...\n");<br />
sleep(5);<br />
printf("[<strong>Thread</strong>] ...thread finished!\n");<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Continua <br />
Aspetti preliminari dei thread POSIX 38
<strong>pthread</strong>s-1b-simple.c (2 <strong>di</strong><br />
2)<br />
int main(void){<br />
<strong>pthread</strong>_t tids[NUM_THREADS];<br />
int i, rc;<br />
printf("[Main] Starting...\n");<br />
for (i=0; i
Esercizio 2<br />
<strong>pthread</strong>s-2a-args.c<br />
analizzare l'output<br />
mo<strong>di</strong>ficare in modo da ottenere un<br />
funzionamento corretto<br />
<strong>pthread</strong>s-2b-args.c<br />
soluzione dell'esercizio precedente<br />
Aspetti preliminari dei thread POSIX 40
<strong>pthread</strong>s-2a-args.c<br />
#include <br />
#include <br />
#include <br />
#include <br />
#define NUM_THREADS 3<br />
void *thread_function(void* arg)<br />
{<br />
int i = *(int*)arg;<br />
printf("[<strong>Thread</strong> %d] Waiting for termination...\n",i);<br />
sleep(5);<br />
printf("[<strong>Thread</strong> %d] ...thread finished!\n",i);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
int main(void)<br />
{<br />
<strong>pthread</strong>_t tids[NUM_THREADS];<br />
int i, rc;<br />
printf("[Main] Starting...\n");<br />
for (i=0; i
Sincronizzazione<br />
(semplice)
Join tra thread<br />
Forma elementare <strong>di</strong> sincronizzazione<br />
il thread che effettua il join si blocca<br />
finché uno specifico thread non termina<br />
il thread che effettua il join può ottenere lo<br />
stato del thread che termina<br />
Attributo detachstate <strong>di</strong> un thread<br />
specifica se si può invocare o no la<br />
funzione join su un certo thread<br />
un thread è joinable per default<br />
Aspetti preliminari dei thread POSIX 43
Operazione <strong>di</strong> join<br />
int int <strong>pthread</strong>_join( <strong>pthread</strong>_t *thread, void **value )<br />
<br />
<br />
<br />
<strong>pthread</strong>_t *thread<br />
identificatore del thread <strong>di</strong> cui attendere la<br />
terminazione<br />
void **value<br />
valore restituito dal thread che termina<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo<br />
EINVAL se il thread da attendere non è joinable<br />
ERSCH se non è stato trovato nessun thread<br />
corrispondente all’identificatore specificato<br />
Aspetti preliminari dei thread POSIX 44
Impostazione attributo <strong>di</strong> join<br />
(1 <strong>di</strong> 4)<br />
int int <strong>pthread</strong>_attr_init( <strong>pthread</strong>_attr_t *attr )<br />
Inizializza gli attributi del <strong>pthread</strong><br />
int int <strong>pthread</strong>_attr_destroy ( <strong>pthread</strong>_attr_t *attr)<br />
Dealloca il <strong>pthread</strong><br />
Aspetti preliminari dei thread POSIX 45
Impostazione attributo <strong>di</strong> join<br />
(2 <strong>di</strong> 4)<br />
Un thread può essere:<br />
Joinable: i thread non sono rilasciati<br />
automaticamente ma rimangono come zombie<br />
finchè altri thread non effettuano delle join<br />
Detached: i thread detached sono rilasciati<br />
automaticamente e non possono essere oggetto<br />
<strong>di</strong> join da parte <strong>di</strong> altri thread.<br />
int int <strong>pthread</strong>_attr_setdetachstate( <strong>pthread</strong>_attr_t *attr,<br />
int int detachstate )<br />
<br />
Detach può essere:<br />
PTHREAD_CREATE_DETACHED<br />
PTHREAD_CREATE_JOINABLE.<br />
Aspetti preliminari dei thread POSIX 46
Impostazione attributo <strong>di</strong> join<br />
(3 <strong>di</strong> 4)<br />
/* /* Attributo Attributo */ */<br />
<strong>pthread</strong>_attr_t <strong>pthread</strong>_attr_t attr; attr;<br />
/* /* Inizializzazione Inizializzazione esplicita esplicita dello dello stato stato joinable joinable */ */<br />
<strong>pthread</strong>_attr_init(&attr);<br />
<strong>pthread</strong>_attr_init(&attr);<br />
<strong>pthread</strong>_attr_setdetachstate(&attr,<br />
<strong>pthread</strong>_attr_setdetachstate(&attr,<br />
PTHREAD_CREATE_JOINABLE);<br />
PTHREAD_CREATE_JOINABLE);<br />
... ...<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
Aspetti preliminari dei thread POSIX 47
Impostazione attributo <strong>di</strong> join<br />
(4 <strong>di</strong> 4)<br />
int int main main (int (int argc, argc, char char *argv[]) *argv[]) {<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t thread[NUM_THREADS];<br />
thread[NUM_THREADS];<br />
... ...<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
for(t=0; for(t=0; t
Esempio 3: thread join (1 <strong>di</strong> 3)<br />
/* /* Include Include */ */<br />
#include #include <br />
<br />
#include #include <br />
#include<br />
#include<br />
<br />
<br />
#define #define NUM_THREADS NUM_THREADS 5<br />
void void *PrintHello(void *PrintHello(void *num) *num) {<br />
printf("\n%d: printf("\n%d: Hello Hello World!\n", World!\n", num); num);<br />
<strong>pthread</strong>_exit(NULL);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Continua <br />
Aspetti preliminari dei thread POSIX 49
Esempio 3: thread join (2 <strong>di</strong> 3)<br />
int int main main (int (int argc, argc, char char *argv[]) *argv[]) {<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threads[NUM_THREADS];<br />
threads[NUM_THREADS];<br />
void void *status; *status;<br />
int int rc, rc, t; t;<br />
<strong>pthread</strong>_attr_t <strong>pthread</strong>_attr_t attr; attr;<br />
/* /* Inizializzazione Inizializzazione esplicita esplicita dello dello stato stato joinable joinable */ */<br />
<strong>pthread</strong>_attr_init(&attr);<br />
<strong>pthread</strong>_attr_init(&attr);<br />
<strong>pthread</strong>_attr_setdetachstate(&attr,<br />
<strong>pthread</strong>_attr_setdetachstate(&attr,<br />
PTHREAD_CREATE_JOINABLE);<br />
PTHREAD_CREATE_JOINABLE);<br />
for(t=0; for(t=0; t
Esempio 3: thread join (3 <strong>di</strong> 3)<br />
for(t=0; for(t=0; t
Sincronizzazione tra thread<br />
Attraverso variabili globali<br />
con<strong>di</strong>vise tra thread<br />
meccanismi <strong>di</strong> protezione<br />
Compito del programmatore<br />
corretto utilizzo delle funzioni <strong>di</strong><br />
sincronizzazione<br />
Meccanismi forniti dalla libreria<br />
semafori <strong>di</strong> mutua esclusione<br />
variabili con<strong>di</strong>tion<br />
semafori generali<br />
Aspetti preliminari dei thread POSIX 52
Semafori
Cosa sono i semafori?<br />
I semafori sono primitive fornite dal<br />
sistema operativo per permettere la<br />
sincronizzazione tra processi e/o<br />
thread.<br />
Aspetti preliminari dei thread POSIX 54
Operazioni sui semafori<br />
In genere sono tre le operazioni che vengono<br />
eseguite da un processo su<br />
un semaforo:<br />
Create: creazione <strong>di</strong> un semaforo.<br />
Wait: attesa su <strong>di</strong> un semaforo dove si verifica il<br />
valore del semaforo<br />
while(sem_value
Semafori <strong>di</strong> mutua esclusione
Cosa sono i mutex? (1 <strong>di</strong> 2)<br />
Una variabile mutex è una variabile<br />
che serve per la protezione delle<br />
sezioni critiche:<br />
variabili con<strong>di</strong>vise mo<strong>di</strong>ficate da più<br />
thread<br />
solo un thread alla volta può accedere ad<br />
una risorsa protetta da un mutex<br />
Il mutex è un semaforo binario cioè il<br />
valore può essere 0 (occupato) oppure<br />
1 (libero)<br />
Aspetti preliminari dei thread POSIX 57
Cosa sono i mutex? (2 <strong>di</strong> 2)<br />
Pensiamo ai mutex come a delle<br />
serrature:<br />
il primo thread che ha accesso alla coda<br />
dei lavori lascia fuori gli altri thread fino a<br />
che non ha portato a termine il suo<br />
compito.<br />
I threads piazzano un mutex nelle<br />
sezioni <strong>di</strong> co<strong>di</strong>ce nelle quali vengono<br />
con<strong>di</strong>visi i dati.<br />
Aspetti preliminari dei thread POSIX 58
Garantire la Mutua Esclusione (1<br />
<strong>di</strong> 2)<br />
Due thread devono decrementare il valore <strong>di</strong><br />
una variabile globale data se questa è<br />
maggiore <strong>di</strong> zero<br />
data = 1<br />
THREAD1<br />
THREAD2<br />
if(data>0)<br />
if(data>0)<br />
data --; data --;<br />
Aspetti preliminari dei thread POSIX 59
Garantire la Mutua Esclusione (2<br />
<strong>di</strong> 2)<br />
A seconda del tempo <strong>di</strong> esecuzione dei due thread, la<br />
variabile data assume valori <strong>di</strong>versi.<br />
Data THREAD1 THREAD2<br />
1 if(data>0)<br />
1 data --;<br />
0 if(data>0)<br />
0 data --;<br />
0 = valore finale <strong>di</strong> data<br />
--------------------------------------------------------<br />
1 if(data>0)<br />
1 if(data>0)<br />
1 data --;<br />
0 data --;<br />
-1 = valore finale <strong>di</strong> data<br />
Aspetti preliminari dei thread POSIX 60
Uso dei mutex<br />
Creare e inizializzare una variabile mutex<br />
Più thread tentano <strong>di</strong> accedere alla risorsa<br />
invocando l’operazione <strong>di</strong> lock<br />
Un solo thread riesce ad acquisire il mutex<br />
mentre gli altri si bloccano<br />
Il thread che ha acquisito il mutex manipola<br />
la risorsa<br />
Lo stesso thread la rilascia<br />
invocando la unlock<br />
Un altro thread acquisisce il mutex e così via<br />
Distruzione della variabile mutex<br />
Aspetti preliminari dei thread POSIX 61
Creazione mutex<br />
<br />
Per creare un mutex è necessario usare<br />
una variabile <strong>di</strong> tipo <strong>pthread</strong>_mutex_t<br />
contenuta nella libreria <strong>pthread</strong><br />
<strong>pthread</strong>_mutex_t è una struttura che<br />
contiene:<br />
Nome del mutex<br />
Proprietario<br />
Contatore<br />
Struttura associata al mutex<br />
<br />
<br />
La coda dei processi sospesi in attesa che mutex sia<br />
libero.<br />
… e simili<br />
Aspetti preliminari dei thread POSIX 62
Inizializzazione mutex<br />
statica<br />
contestuale alla <strong>di</strong>chiarazione<br />
<strong>di</strong>namica<br />
attraverso<br />
<strong>pthread</strong>_mutex_t mutex;<br />
<strong>pthread</strong>_mutex_init (&mutex, NULL);<br />
Aspetti preliminari dei thread POSIX 63
Inizializzazione statica<br />
<br />
Per il tipo <strong>di</strong> dato <strong>pthread</strong>_mutex_t, è definita la<br />
macro <strong>di</strong> inizializzazione<br />
PTHREAD_MUTEX_INITIALIZER<br />
<br />
Il mutex è un tipo definito "ad hoc" per gestire la<br />
mutua esclusione quin<strong>di</strong> il valore iniziale può<br />
essergli assegnato anche in modo statico me<strong>di</strong>ante<br />
questa macro.<br />
/* /* Variabili globali */ */<br />
<strong>pthread</strong>_mutex_t amutex = PTHREAD_MUTEX_INITIALIZER;<br />
Aspetti preliminari dei thread POSIX 64
Inizializzazione <strong>di</strong>namica<br />
<strong>pthread</strong>_mutex_t mutex;<br />
int int <strong>pthread</strong>_mutex_init( <strong>pthread</strong>_mutex_t *mutex, const<br />
<strong>pthread</strong>_mutexattr_t *mattr )<br />
<br />
<br />
<br />
<strong>pthread</strong>_mutex_t *mutex<br />
puntatore al mutex da inizializzare<br />
<strong>pthread</strong>_mutexattr_t *mattr<br />
attributi del mutex da inizializzare<br />
se NULL usa valori default<br />
Valore <strong>di</strong> ritorno<br />
sempre il valore 0<br />
Aspetti preliminari dei thread POSIX 65
Interfacce<br />
Su mutex sono possibili solo due<br />
operazioni: locking e unlocking<br />
(equivalenti a <strong>wait</strong> e signal sui<br />
semafori)<br />
Aspetti preliminari dei thread POSIX 66
Interfaccia: Lock<br />
Ogni thread, prima <strong>di</strong> accedere ai dati<br />
con<strong>di</strong>visi, deve effettuare la lock su una<br />
stessa variabile mutex.<br />
Blocca l’accesso da parte <strong>di</strong> altri thread.<br />
Se più thread eseguono l’operazione <strong>di</strong> lock<br />
su una stessa variabile mutex, solo uno dei<br />
thread termina la lock e prosegue<br />
l’esecuzione, gli altri rimangono bloccati<br />
nella lock. In tal modo, il processo che<br />
continua l’esecuzione può accedere ai dati<br />
(protetti me<strong>di</strong>ante la mutex).<br />
Aspetti preliminari dei thread POSIX 67
Operazioni: lock e trylock<br />
lock<br />
bloccante (standard)<br />
trylock<br />
non bloccante (utile per evitare deadlock)<br />
è come la lock() ma se si accorge che il mutex è<br />
già in possesso <strong>di</strong> un altro thread (e quin<strong>di</strong> si<br />
rimarrebbe bloccati) restituisce imme<strong>di</strong>atamente<br />
il controllo al chiamante con risultato EBUSY<br />
Una situazione <strong>di</strong> deadlock si verifica quando uno o più thread sono<br />
bloccati aspettando un evento che non si verificherà mai.<br />
Aspetti preliminari dei thread POSIX 68
lock<br />
int int <strong>pthread</strong>_mutex_lock( <strong>pthread</strong>_mutex_t *mutex )<br />
<strong>pthread</strong>_mutex_t *mutex<br />
puntatore al mutex da bloccare<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo<br />
<strong>di</strong>verso da 0 altrimenti<br />
Aspetti preliminari dei thread POSIX 69
trylock<br />
int int <strong>pthread</strong>_mutex_trylock( <strong>pthread</strong>_mutex_t *mutex )<br />
<br />
<br />
<strong>pthread</strong>_mutex_t *mutex<br />
puntatore al mutex da bloccare<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo e si ottenga la proprietà<br />
della mutex<br />
EBUSY se il mutex è occupato<br />
Aspetti preliminari dei thread POSIX 70
Interfaccia: Unlock<br />
Libera la variabile mutex.<br />
Un altro thread che ha<br />
precedentemente eseguito la lock<br />
della mutex potrà allora terminare la<br />
lock ed accedere a sua volta ai dati.<br />
Aspetti preliminari dei thread POSIX 71
unlock<br />
int int <strong>pthread</strong>_mutex_unlock( <strong>pthread</strong>_mutex_t *mutex )<br />
<strong>pthread</strong>_mutex_t *mutex<br />
puntatore al mutex da sbloccare<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo<br />
Aspetti preliminari dei thread POSIX 72
destroy<br />
int int <strong>pthread</strong>_mutex_destroy( <strong>pthread</strong>_mutex_t *mutex )<br />
Elimina il mutex<br />
<strong>pthread</strong>_mutex_t *mutex<br />
puntatore al mutex da <strong>di</strong>struggere<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo<br />
EBUSY se il mutex è occupato<br />
Aspetti preliminari dei thread POSIX 73
Esempio 4: uso dei mutex (1 <strong>di</strong> 2)<br />
#include #include <br />
<br />
int int a=1, a=1, b=1; b=1;<br />
<strong>pthread</strong>_mutex_t <strong>pthread</strong>_mutex_t m = PTHREAD_MUTEX_INITIALIZER;<br />
PTHREAD_MUTEX_INITIALIZER;<br />
void* void* thread1(void thread1(void *arg) *arg) {<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
printf(“Primo printf(“Primo thread thread (parametro: (parametro: %d)\n", %d)\n", *(int*)arg);<br />
*(int*)arg);<br />
a++; a++; b++; b++;<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
}<br />
}<br />
void* void* thread2(void thread2(void *arg) *arg) {<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
printf(“Secondo<br />
printf(“Secondo<br />
thread<br />
thread<br />
(parametro:<br />
(parametro:<br />
%d)\n",<br />
%d)\n",<br />
*(int*)arg);<br />
*(int*)arg);<br />
b=b*2; b=b*2; a=a*2; a=a*2;<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
} Continua<br />
Aspetti preliminari dei thread POSIX 74
Esempio 4: uso dei mutex (2 <strong>di</strong> 2)<br />
main() main() {<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threa<strong>di</strong>d1, threa<strong>di</strong>d1, threa<strong>di</strong>d2;<br />
threa<strong>di</strong>d2;<br />
int int i = 1, 1, j=2; j=2;<br />
<strong>pthread</strong>_create(&threa<strong>di</strong>d1, <strong>pthread</strong>_create(&threa<strong>di</strong>d1, NULL, NULL, thread1, thread1, (void (void *)&i); *)&i);<br />
<strong>pthread</strong>_create(&threa<strong>di</strong>d2, <strong>pthread</strong>_create(&threa<strong>di</strong>d2, NULL, NULL, thread2, thread2, (void (void *)&j); *)&j);<br />
<strong>pthread</strong>_join(threa<strong>di</strong>d1, <strong>pthread</strong>_join(threa<strong>di</strong>d1, NULL); NULL);<br />
<strong>pthread</strong>_join(threa<strong>di</strong>d2, <strong>pthread</strong>_join(threa<strong>di</strong>d2, NULL); NULL);<br />
printf("Valori<br />
printf("Valori<br />
finali:<br />
finali:<br />
a=%d<br />
a=%d<br />
b=%d\n",<br />
b=%d\n",<br />
a,<br />
a,<br />
b);<br />
b);<br />
}<br />
Tratto con mo<strong>di</strong>fiche da:<br />
http://www.univ.trieste.it/~mumolo/posix2.p<br />
df<br />
Aspetti preliminari dei thread POSIX 75
Esempio 5: inizializzazione <strong>di</strong>namica<br />
(1 <strong>di</strong> 2)<br />
#include #include <br />
<br />
int int a=1, a=1, b=1; b=1;<br />
<strong>pthread</strong>_mutex_t <strong>pthread</strong>_mutex_t m; m;<br />
void* void* thread1(void thread1(void *arg) *arg) {<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
printf(“Primo printf(“Primo thread thread (parametro: (parametro: %d)\n", %d)\n", *(int*)arg);<br />
*(int*)arg);<br />
a++; a++; b++; b++;<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
}<br />
}<br />
void* void* thread2(void thread2(void *arg) *arg) {<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
<strong>pthread</strong>_mutex_lock(&m);<br />
printf(“Secondo<br />
printf(“Secondo<br />
thread<br />
thread<br />
(parametro:<br />
(parametro:<br />
%d)\n",<br />
%d)\n",<br />
*(int*)arg);<br />
*(int*)arg);<br />
b=b*2; b=b*2; a=a*2; a=a*2;<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
<strong>pthread</strong>_mutex_unlock(&m);<br />
} Continua<br />
Aspetti preliminari dei thread POSIX 76
Esempio 5: inizializzazione <strong>di</strong>namica<br />
(2 <strong>di</strong> 2)<br />
main() main() {<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threa<strong>di</strong>d1, threa<strong>di</strong>d1, threa<strong>di</strong>d2;<br />
threa<strong>di</strong>d2;<br />
int int i = 1, 1, j=2; j=2;<br />
<strong>pthread</strong>_mutex_init(&m, <strong>pthread</strong>_mutex_init(&m, NULL); NULL);<br />
<strong>pthread</strong>_create(&threa<strong>di</strong>d1, <strong>pthread</strong>_create(&threa<strong>di</strong>d1, NULL, NULL, thread1, thread1, (void (void *)&i); *)&i);<br />
<strong>pthread</strong>_create(&threa<strong>di</strong>d2,<br />
<strong>pthread</strong>_create(&threa<strong>di</strong>d2,<br />
NULL,<br />
NULL,<br />
thread2,<br />
thread2,<br />
(void<br />
(void<br />
*)&j);<br />
*)&j);<br />
<strong>pthread</strong>_join(threa<strong>di</strong>d1, <strong>pthread</strong>_join(threa<strong>di</strong>d1, NULL); NULL);<br />
<strong>pthread</strong>_join(threa<strong>di</strong>d2, <strong>pthread</strong>_join(threa<strong>di</strong>d2, NULL); NULL);<br />
printf("Valori printf("Valori finali: finali: a=%d a=%d b=%d\n", b=%d\n", a, a, b); b);<br />
<strong>pthread</strong>_mutex_destroy(&m);<br />
<strong>pthread</strong>_mutex_destroy(&m);<br />
}<br />
Aspetti preliminari dei thread POSIX 77
Esempio 6 (1 <strong>di</strong> 3)<br />
/* /* esempio utilizzo dei dei Mutex */ */<br />
#include <br />
#include <br />
<strong>pthread</strong>_mutex_t mymutex;<br />
void *body(void *arg){<br />
int int i,j;<br />
for for (j=0; j
Esempio 6 (2 <strong>di</strong> 3)<br />
int int main(){<br />
<strong>pthread</strong>_t t1,t2,t3;<br />
<strong>pthread</strong>_attr_t myattr;<br />
int int err;<br />
<strong>pthread</strong>_mutexattr_t mymutexattr;<br />
<strong>pthread</strong>_mutexattr_init(&mymutexattr);<br />
<strong>pthread</strong>_mutex_init(&mymutex, &mymutexattr);<br />
<strong>pthread</strong>_mutexattr_destroy(&mymutexattr);<br />
<strong>pthread</strong>_attr_init(&myattr);<br />
…<br />
Continua<br />
<br />
Aspetti preliminari dei thread POSIX 79
Esempio 6 (3 <strong>di</strong> 3)<br />
err err = <strong>pthread</strong>_create(&t1, &myattr, body, body, (void (void *)".");<br />
err err = <strong>pthread</strong>_create(&t2, &myattr, body, body, (void (void *)"#");<br />
err err = <strong>pthread</strong>_create(&t3, &myattr, body, body, (void (void *)"o");<br />
<strong>pthread</strong>_attr_destroy(&myattr);<br />
}<br />
<strong>pthread</strong>_join(t1, NULL);<br />
<strong>pthread</strong>_join(t2, NULL);<br />
<strong>pthread</strong>_join(t3, NULL);<br />
printf("\n");<br />
return 0; 0;<br />
Aspetti preliminari dei thread POSIX 80
Esercizio 3<br />
<strong>pthread</strong>s-3a-mutex.c<br />
analizzare l'output<br />
mo<strong>di</strong>ficare in modo da ottenere un<br />
funzionamento corretto<br />
<strong>pthread</strong>s-3b-mutex.c<br />
soluzione dell'esercizio precedente<br />
Aspetti preliminari dei thread POSIX 81
<strong>pthread</strong>s-3a-mutex.c<br />
#include <br />
#include <br />
#define NUM_THREADS 40<br />
int shared = 0;<br />
void *thread_main(void* arg){<br />
int i,k;<br />
for (i=0; i
Variabili con<strong>di</strong>tion
Con<strong>di</strong>tion vs Semafori<br />
Le variabili con<strong>di</strong>tion sono molto <strong>di</strong>verse dai<br />
semafori <strong>di</strong> sincronizzazione, anche se<br />
semanticamente fanno la stessa cosa<br />
Le primitive delle con<strong>di</strong>tion si preoccupano <strong>di</strong><br />
rilasciare la mutua esclusione prima <strong>di</strong><br />
bloccarsi e ed riacquisirla dopo essere state<br />
sbloccate<br />
I semafori generali, invece, prescindono dalla<br />
presenza <strong>di</strong> altri meccanismi<br />
Aspetti preliminari dei thread POSIX 84
Cosa sono le variabili con<strong>di</strong>tion<br />
Strumento <strong>di</strong> sincronizzazione: consente<br />
la sospensione dei thread in attesa che sia<br />
sod<strong>di</strong>sfatta una con<strong>di</strong>zione logica.<br />
Una con<strong>di</strong>tion variable è utilizzata per<br />
sospendere l'esecuzione <strong>di</strong> un thread in<br />
attesa che si verifichi un certo evento.<br />
Ad ogni con<strong>di</strong>tion viene associata una coda<br />
per la sospensione dei thread.<br />
La variabile con<strong>di</strong>zione non ha uno stato,<br />
rappresenta solo una coda <strong>di</strong> thread.<br />
Aspetti preliminari dei thread POSIX 85
Variabili con<strong>di</strong>tion<br />
Attraverso le variabili con<strong>di</strong>tion è<br />
possibile implementare con<strong>di</strong>zioni più<br />
complesse che i thread devono<br />
sod<strong>di</strong>sfare per essere eseguiti.<br />
Linux garantisce che i thread bloccati<br />
su una con<strong>di</strong>zione vengano sbloccati<br />
quando essa cambia.<br />
Aspetti preliminari dei thread POSIX 86
Mutua esclusione<br />
Una variabile con<strong>di</strong>zione non fornisce<br />
la mutua esclusione.<br />
C'è bisogno <strong>di</strong> un mutex per poter<br />
sincronizzare l'accesso ai dati.<br />
Aspetti preliminari dei thread POSIX 87
Sincronizzazione<br />
Una variabile con<strong>di</strong>tion è sempre associata<br />
ad un mutex<br />
un thread ottiene il mutex e testa il pre<strong>di</strong>cato<br />
se il pre<strong>di</strong>cato è verificato allora il thread<br />
esegue le sue operazioni e rilascia il mutex<br />
se il pre<strong>di</strong>cato non è verificato, in modo<br />
atomico<br />
il mutex viene rilasciato (implicitamente)<br />
il thread si blocca sulla variabile con<strong>di</strong>tion<br />
un thread bloccato riacquisisce il mutex nel<br />
momento in cui viene svegliato da un altro<br />
thread<br />
Aspetti preliminari dei thread POSIX 88
Creazione con<strong>di</strong>tion<br />
Oggetti <strong>di</strong> sincronizzazione su cui un<br />
processo si può bloccare in attesa<br />
associate ad una con<strong>di</strong>zione logica<br />
arbitraria<br />
generalizzazione dei semafori<br />
nuovo tipo <strong>pthread</strong>_cond_t<br />
attributi variabili con<strong>di</strong>zione <strong>di</strong> tipo<br />
<strong>pthread</strong>_condattr_t<br />
Aspetti preliminari dei thread POSIX 89
Inizializzazione statica<br />
<strong>pthread</strong>_cond_t cond = PTHREAD_COND_INITIALIZER;<br />
• Per il tipo <strong>di</strong> dato <strong>pthread</strong>_cond_t, è definita<br />
la macro <strong>di</strong> inizializzazione<br />
PTHREAD_COND_INITIALIZER<br />
Aspetti preliminari dei thread POSIX 90
Inizializzazione <strong>di</strong>namica<br />
int int <strong>pthread</strong>_cond_init( <strong>pthread</strong>_cond_t *cond,<br />
<strong>pthread</strong>_condattr_t *cond_attr )<br />
<br />
<br />
<strong>pthread</strong>_cond_t *cond<br />
puntatore ad un’istanza <strong>di</strong> con<strong>di</strong>tion che<br />
rappresenta la con<strong>di</strong>zione <strong>di</strong> sincronizzazione<br />
<strong>pthread</strong>_condattr_t *cond_attr<br />
punta a una struttura che contiene gli attributi<br />
della con<strong>di</strong>zione<br />
se NULL usa valori <strong>di</strong> default<br />
Aspetti preliminari dei thread POSIX 91
Distruzione variabili con<strong>di</strong>tion<br />
int int <strong>pthread</strong>_cond_destroy( <strong>pthread</strong>_cond_t *cond )<br />
Dealloca tutte le risorse allocate per gestire la<br />
variabile con<strong>di</strong>zione specificata<br />
Non devono esistere thread in attesa della<br />
con<strong>di</strong>zione<br />
<strong>pthread</strong>_cond_t *cond<br />
puntatore ad un’istanza <strong>di</strong> con<strong>di</strong>tion da <strong>di</strong>struggere<br />
Valore <strong>di</strong> ritorno<br />
0 in caso <strong>di</strong> successo oppure un co<strong>di</strong>ce d’errore ≠0<br />
Aspetti preliminari dei thread POSIX 92
Interfacce<br />
Operazioni fondamentali:<br />
<strong>wait</strong> (sospensione)<br />
signal ( risveglio)<br />
Aspetti preliminari dei thread POSIX 93
Interfaccia <strong>wait</strong><br />
La <strong>wait</strong> serve per sincronizzarsi con una certa<br />
con<strong>di</strong>zione all'interno <strong>di</strong> un blocco <strong>di</strong> dati con<strong>di</strong>visi e<br />
protetti da un mutex<br />
La presenza del mutex fra i parametri garantisce<br />
che, al momento del bloccaggio, esso venga<br />
liberato, eliminando a monte possibili errori <strong>di</strong><br />
programmazione che potrebbero condurre a<br />
con<strong>di</strong>zioni <strong>di</strong> deadlock.<br />
Se la <strong>wait</strong> ritorna in modo regolare, è garantito che<br />
la mutua esclusione, sul semaforo mutex passatole,<br />
sia stata nuovamente acquisita.<br />
Aspetti preliminari dei thread POSIX 94
<strong>wait</strong><br />
int int <strong>pthread</strong>_cond_<strong>wait</strong>( <strong>pthread</strong>_cond_t *cond,<br />
<strong>pthread</strong>_mutex_t *mutex )<br />
<br />
<br />
<br />
<strong>pthread</strong>_cond_t *cond<br />
puntatore ad un’istanza <strong>di</strong> con<strong>di</strong>tion che<br />
rappresenta la con<strong>di</strong>zione <strong>di</strong> sincronizzazione<br />
puntatore all’oggetto con<strong>di</strong>zione su cui bloccarsi<br />
<strong>pthread</strong>_mutex_t *mutex<br />
l'in<strong>di</strong>rizzo <strong>di</strong> un semaforo <strong>di</strong> mutua esclusione<br />
necessario alla corretta consistenza dei dati<br />
Valore <strong>di</strong> ritorno<br />
sempre 0<br />
Aspetti preliminari dei thread POSIX 95
Interfaccia signal<br />
La signal non si preoccupa <strong>di</strong> liberare la<br />
mutua esclusione, infatti, fra i suoi parametri<br />
non c'è il mutex<br />
Il mutex deve essere rilasciato<br />
esplicitamente, altrimenti si potrebbe<br />
produrre una con<strong>di</strong>zione <strong>di</strong> deadlock.<br />
Due varianti<br />
Standard: sblocca un solo thread bloccato<br />
Broadcast: sblocca tutti i thread bloccati<br />
Aspetti preliminari dei thread POSIX 96
signal<br />
int int <strong>pthread</strong>_cond_signal ( <strong>pthread</strong>_cond_t *cond)<br />
Se esistono thread sospesi nella coda associata a<br />
cond, viene risvegliato il primo.<br />
Se non vi sono thread sospesi sulla con<strong>di</strong>zione, la<br />
signal non ha effetto.<br />
<strong>pthread</strong>_cond_t *cond<br />
puntatore all’oggetto con<strong>di</strong>zione<br />
Valore <strong>di</strong> ritorno<br />
sempre 0<br />
Aspetti preliminari dei thread POSIX 97
oadcast<br />
int int <strong>pthread</strong>_cond_broadcast ( <strong>pthread</strong>_cond_t *cond )<br />
Se esistono thread sospesi nella coda<br />
associata a cond, vengono svegliati tutti<br />
altrimenti nessun effetto<br />
<strong>pthread</strong>_cond_t *cond<br />
puntatore all’oggetto con<strong>di</strong>zione<br />
Valore <strong>di</strong> ritorno<br />
sempre 0<br />
Aspetti preliminari dei thread POSIX 98
Valutazione con<strong>di</strong>zione<br />
Il thread svegliato deve rivalutare la con<strong>di</strong>zione<br />
l’altro thread potrebbe non aver testato la<br />
con<strong>di</strong>zione<br />
la con<strong>di</strong>zione potrebbe essere cambiata nel<br />
frattempo<br />
possono verificarsi wakeup “spuri”<br />
<strong>pthread</strong>_mutex_lock(&mutex);<br />
while(!con<strong>di</strong>tion_to_hold)<br />
ptread_cond_<strong>wait</strong>(&cond, &mutex);<br />
computation();<br />
<strong>pthread</strong>_mutex_unlock(&mutex);<br />
Aspetti preliminari dei thread POSIX 99
Stato della coda<br />
Non è prevista una funzione per<br />
verificare lo stato della coda associata<br />
a una con<strong>di</strong>zione.<br />
Aspetti preliminari dei thread POSIX 100
Esempio <strong>di</strong> utilizzo (1 <strong>di</strong> 2)<br />
Risorsa che può essere usata<br />
contemporaneamente da MAX thread.<br />
con<strong>di</strong>tion PIENO per la sospensione dei thread<br />
M mutex associato a pieno<br />
N_int numero <strong>di</strong> thread che stanno utilizzando la<br />
risorsa<br />
#define MAX 100<br />
/*variabili globali*/<br />
int N_in=0 /*numero thread che stanno utilizzando la risorsa*/<br />
<strong>pthread</strong>_cond_t PIENO;<br />
<strong>pthread</strong>_ mutex M;/*mutex associato alla cond. PIENO*/<br />
Aspetti preliminari dei thread POSIX 101
Esempio <strong>di</strong> utilizzo (2 <strong>di</strong> 2)<br />
void co<strong>di</strong>ce_thread() {<br />
/*fase <strong>di</strong> entrata*/<br />
<strong>pthread</strong>_mutex_lock(&M);<br />
/* controlla la con<strong>di</strong>zione <strong>di</strong> ingresso*/<br />
if(N_in == MAX)<br />
<strong>pthread</strong>_cond_<strong>wait</strong>(&PIENO,&M);<br />
/*aggiorna lo stato della risorsa */<br />
N_in ++;<br />
<strong>pthread</strong>_mutex_unlock(&M);<br />
<br />
/*fase <strong>di</strong> uscita*/<br />
<strong>pthread</strong>_mutex_lock(&M);<br />
/*aggiorna lo stato della risorsa */<br />
N_in --;<br />
<strong>pthread</strong>_cond_signal(&PIENO);<br />
<strong>pthread</strong>_mutex_unlock(&M);<br />
}<br />
Aspetti preliminari dei thread POSIX 102
Esempio 8 (1 <strong>di</strong> 4)<br />
#include #include <br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
/* /* mutex mutex */ */<br />
<strong>pthread</strong>_mutex_t <strong>pthread</strong>_mutex_t con<strong>di</strong>tion_mutex con<strong>di</strong>tion_mutex = PTHREAD_MUTEX_INITIALIZER;<br />
PTHREAD_MUTEX_INITIALIZER;<br />
/* /* con<strong>di</strong>tion con<strong>di</strong>tion variable*/<br />
variable*/<br />
<strong>pthread</strong>_cond_t<br />
<strong>pthread</strong>_cond_t<br />
con<strong>di</strong>tion_cond<br />
con<strong>di</strong>tion_cond<br />
=<br />
=<br />
PTHREAD_COND_INITIALIZER;<br />
PTHREAD_COND_INITIALIZER;<br />
Continua<br />
<br />
Aspetti preliminari dei thread POSIX 103
Esempio 8 (2 <strong>di</strong> 4)<br />
void void thread1_func(void thread1_func(void *ptr) *ptr) {<br />
printf("Avvio printf("Avvio dell’esecuzione dell’esecuzione del del %s.\n",(char %s.\n",(char *)ptr); *)ptr);<br />
sleep(2); sleep(2); /* /* pausa pausa <strong>di</strong> <strong>di</strong> 2 secon<strong>di</strong> secon<strong>di</strong> */ */<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 1 in in procinto procinto <strong>di</strong> <strong>di</strong> entrare entrare nella nella sezione sezione critica.\n");<br />
critica.\n");<br />
<strong>pthread</strong>_mutex_lock(&con<strong>di</strong>tion_mutex);<br />
<strong>pthread</strong>_mutex_lock(&con<strong>di</strong>tion_mutex);<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 1 nella nella sezione sezione critica.\n");<br />
critica.\n");<br />
printf("<strong>Thread</strong><br />
printf("<strong>Thread</strong><br />
1<br />
1<br />
si<br />
si<br />
sospende<br />
sospende<br />
sulla<br />
sulla<br />
con<strong>di</strong>tion<br />
con<strong>di</strong>tion<br />
variable.\n");<br />
variable.\n");<br />
<strong>pthread</strong>_cond_<strong>wait</strong>(&con<strong>di</strong>tion_cond, <strong>pthread</strong>_cond_<strong>wait</strong>(&con<strong>di</strong>tion_cond, &con<strong>di</strong>tion_mutex);<br />
&con<strong>di</strong>tion_mutex);<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 1 riprende riprende l’esecuzione.\n");<br />
l’esecuzione.\n");<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 1 in in procinto procinto <strong>di</strong> <strong>di</strong> uscire uscire dalla dalla sezione sezione critica.\n");<br />
critica.\n");<br />
<strong>pthread</strong>_mutex_unlock(&con<strong>di</strong>tion_mutex);<br />
<strong>pthread</strong>_mutex_unlock(&con<strong>di</strong>tion_mutex);<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 1 in in procinto procinto <strong>di</strong> <strong>di</strong> terminare.\n");<br />
terminare.\n");<br />
}<br />
}<br />
Continua<br />
<br />
Aspetti preliminari dei thread POSIX 104
Esempio 8 (3 <strong>di</strong> 4)<br />
void void thread2_func(void thread2_func(void *ptr) *ptr) {<br />
printf("Avvio printf("Avvio dell’esecuzione dell’esecuzione del del %s.\n",(char %s.\n",(char *)ptr); *)ptr);<br />
sleep(5); sleep(5); /* /* pausa pausa <strong>di</strong> <strong>di</strong> 5 secon<strong>di</strong> secon<strong>di</strong> */ */<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 2 in in procinto procinto <strong>di</strong> <strong>di</strong> entrare entrare nella nella sezione sezione critica.\n");<br />
critica.\n");<br />
<strong>pthread</strong>_mutex_lock(&con<strong>di</strong>tion_mutex);<br />
<strong>pthread</strong>_mutex_lock(&con<strong>di</strong>tion_mutex);<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 2 nella nella sezione sezione critica.\n");<br />
critica.\n");<br />
printf("<strong>Thread</strong><br />
printf("<strong>Thread</strong><br />
2<br />
2<br />
segnala<br />
segnala<br />
l’evento<br />
l’evento<br />
della<br />
della<br />
con<strong>di</strong>tion<br />
con<strong>di</strong>tion<br />
variable.\n");<br />
variable.\n");<br />
<strong>pthread</strong>_cond_signal(&con<strong>di</strong>tion_cond);<br />
<strong>pthread</strong>_cond_signal(&con<strong>di</strong>tion_cond);<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 2 in in procinto procinto <strong>di</strong> <strong>di</strong> uscire uscire dalla dalla sezione sezione critica.\n");<br />
critica.\n");<br />
<strong>pthread</strong>_mutex_unlock(&con<strong>di</strong>tion_mutex);<br />
<strong>pthread</strong>_mutex_unlock(&con<strong>di</strong>tion_mutex);<br />
}<br />
printf("<strong>Thread</strong> printf("<strong>Thread</strong> 2 in in procinto procinto <strong>di</strong> <strong>di</strong> terminare.\n");<br />
terminare.\n");<br />
Continua<br />
<br />
Aspetti preliminari dei thread POSIX 105
Esempio 8 (4 <strong>di</strong> 4)<br />
main() main() {<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t thread1,thread2;<br />
thread1,thread2;<br />
char char *msg1="<strong>Thread</strong> *msg1="<strong>Thread</strong> 1"; 1";<br />
char char *msg2="<strong>Thread</strong> *msg2="<strong>Thread</strong> 2"; 2";<br />
if(<strong>pthread</strong>_create(&thread1,NULL,(void if(<strong>pthread</strong>_create(&thread1,NULL,(void *)&thread1_func,(void *)&thread1_func,(void *)msg1)!=0){<br />
*)msg1)!=0){<br />
perror("Errore perror("Errore nella nella creazione creazione del del primo primo thread.\n");<br />
thread.\n");<br />
exit(1); exit(1);<br />
}<br />
}<br />
if(<strong>pthread</strong>_create(&thread2,NULL,(void if(<strong>pthread</strong>_create(&thread2,NULL,(void *)&thread2_func,(void *)&thread2_func,(void *)msg2)!=0){<br />
*)msg2)!=0){<br />
perror("Errore perror("Errore nella nella creazione creazione del del secondo secondo thread.\n");<br />
thread.\n");<br />
exit(1); exit(1);<br />
}<br />
<strong>pthread</strong>_join(thread1,NULL);<br />
<strong>pthread</strong>_join(thread1,NULL);<br />
<strong>pthread</strong>_join(thread2,NULL);<br />
<strong>pthread</strong>_join(thread2,NULL);<br />
exit(0); exit(0);<br />
}<br />
Aspetti preliminari dei thread POSIX 106
Esempio 9: incremento contatore<br />
3)<br />
(1 <strong>di</strong><br />
void void *inc_count(void *inc_count(void *idp) *idp) {<br />
int int j,i; j,i; double double result=0.0; result=0.0; int int *my_id *my_id = idp; idp;<br />
for for (i=0; (i=0; i
Esempio 9: incremento contatore<br />
3)<br />
(2 <strong>di</strong><br />
void void *watch_count(void *watch_count(void *idp) *idp) {<br />
int int *my_id *my_id = idp; idp;<br />
printf("Starting printf("Starting watch_count(): watch_count(): thread thread %d\n", %d\n", *my_id); *my_id);<br />
<strong>pthread</strong>_mutex_lock(&count_mutex); <strong>pthread</strong>_mutex_lock(&count_mutex); /*Lock /*Lock mutex mutex and and <strong>wait</strong> <strong>wait</strong> for for signal. signal. */ */<br />
while while (count
Esempio 9: incremento contatore<br />
3)<br />
(3 <strong>di</strong><br />
int int main main (int (int argc, argc, char char *argv[]) *argv[]) {<br />
int int i, i, res; res;<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t threads[3];<br />
threads[3];<br />
<strong>pthread</strong>_attr_t <strong>pthread</strong>_attr_t attr; attr;<br />
/* /* Initialize Initialize mutex mutex and and con<strong>di</strong>tion con<strong>di</strong>tion variable variable objects objects */ */<br />
<strong>pthread</strong>_mutex_init(&count_mutex, <strong>pthread</strong>_mutex_init(&count_mutex, NULL); NULL);<br />
<strong>pthread</strong>_cond_init <strong>pthread</strong>_cond_init (&count_threshold_cv, (&count_threshold_cv, NULL); NULL);<br />
/*<br />
/*<br />
Create<br />
Create<br />
threads<br />
threads<br />
and<br />
and<br />
<strong>wait</strong><br />
<strong>wait</strong><br />
for<br />
for<br />
all<br />
all<br />
threads<br />
threads<br />
to<br />
to<br />
complete<br />
complete<br />
*/<br />
*/<br />
…<br />
/* /* Clean Clean up up and and exit exit */ */<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
<strong>pthread</strong>_attr_destroy(&attr);<br />
<strong>pthread</strong>_mutex_destroy(&count_mutex);<br />
<strong>pthread</strong>_mutex_destroy(&count_mutex);<br />
<strong>pthread</strong>_cond_destroy(&count_threshold_cv);<br />
<strong>pthread</strong>_cond_destroy(&count_threshold_cv);<br />
<strong>pthread</strong>_exit(NULL);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Aspetti preliminari dei thread POSIX 109
Esempio 10 (1 <strong>di</strong> 6)<br />
#include<br />
#include<br />
<br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
#include #include <br />
<br />
/* /* Numero Numero <strong>di</strong> <strong>di</strong> cicli cicli <strong>di</strong> <strong>di</strong> lettura/scrittura lettura/scrittura che che vengono vengono fatti fatti dai dai thread thread */ */<br />
#define #define CICLI CICLI 1<br />
/* /* Lunghezza Lunghezza del del buffer buffer */ */<br />
#define #define LUN LUN 20 20<br />
/* /* Numero Numero <strong>di</strong> <strong>di</strong> cicli cicli <strong>di</strong> <strong>di</strong> attesa attesa a vuoto vuoto <strong>di</strong> <strong>di</strong> uno uno scrittore scrittore */ */<br />
#define #define DELAY_WRITER DELAY_WRITER 200000 200000<br />
/* /* Numero Numero <strong>di</strong> <strong>di</strong> cicli cicli <strong>di</strong> <strong>di</strong> attesa attesa a vuoto vuoto <strong>di</strong> <strong>di</strong> uno uno scrittore scrittore */ */<br />
#define #define DELAY_READER DELAY_READER 2000000 2000000<br />
Aspetti preliminari dei thread POSIX 110
Esempio 10 (2 <strong>di</strong> 6)<br />
/*<br />
/*<br />
Memoria<br />
Memoria<br />
Con<strong>di</strong>visa<br />
Con<strong>di</strong>visa<br />
fra<br />
fra<br />
i<br />
i<br />
thread<br />
thread<br />
...<br />
...<br />
*/<br />
*/<br />
struct struct {<br />
/* /* Semaforo Semaforo <strong>di</strong> <strong>di</strong> mutua mutua esclusione esclusione */ */<br />
<strong>pthread</strong>_mutex_t <strong>pthread</strong>_mutex_t mutex; mutex;<br />
/* /* Variabile Variabile con<strong>di</strong>tion con<strong>di</strong>tion per per il il lettore lettore */ */<br />
<strong>pthread</strong>_cond_t <strong>pthread</strong>_cond_t lettore; lettore;<br />
/* /* Variabile Variabile con<strong>di</strong>tion con<strong>di</strong>tion per per gli gli scrittori scrittori */ */<br />
<strong>pthread</strong>_cond_t <strong>pthread</strong>_cond_t scrittori;<br />
scrittori;<br />
/* /* Buffer Buffer */ */<br />
char char scritta[LUN+1];<br />
scritta[LUN+1];<br />
/* /* Variabili Variabili per per la la gestione gestione del del buffer buffer */ */<br />
int int primo, primo, ultimo, ultimo, elementi;<br />
elementi;<br />
/* /* Numero Numero <strong>di</strong> <strong>di</strong> lettori lettori e scrittori scrittori bloccati bloccati */ */<br />
int int blockscri, blockscri, blocklet;<br />
blocklet;<br />
} shared shared = {PTHREAD_MUTEX_INITIALIZER,<br />
{PTHREAD_MUTEX_INITIALIZER,<br />
PTHREAD_COND_INITIALIZER,<br />
PTHREAD_COND_INITIALIZER,<br />
PTHREAD_COND_INITIALIZER};<br />
PTHREAD_COND_INITIALIZER};<br />
Aspetti preliminari dei thread POSIX 111
Esempio 10 (3 <strong>di</strong> 6)<br />
int<br />
int<br />
main(void){<br />
main(void){<br />
<strong>pthread</strong>_t <strong>pthread</strong>_t s1TID, s1TID, s2TID, s2TID, lTID; lTID;<br />
int int res, res, i; i;<br />
/* /* Inizializzo Inizializzo la la stringa stringa scritta scritta */ */<br />
for(i=0; for(i=0; i
Esempio 10 (4 <strong>di</strong> 6)<br />
res<br />
res<br />
=<br />
=<br />
<strong>pthread</strong>_create(&lTID,<br />
<strong>pthread</strong>_create(&lTID,<br />
NULL,<br />
NULL,<br />
lettore,<br />
lettore,<br />
NULL);<br />
NULL);<br />
if if (res (res != != 0) 0) printf("Errore printf("Errore nella nella creazione creazione del del primo primo thread\n");<br />
thread\n");<br />
res res = <strong>pthread</strong>_create(&s1TID, <strong>pthread</strong>_create(&s1TID, NULL, NULL, scrittore1, scrittore1, NULL); NULL);<br />
if if (res (res != != 0) 0) {<br />
printf("Errore printf("Errore nella nella creazione creazione del del secondo secondo thread\n");<br />
thread\n");<br />
<strong>pthread</strong>_kill(s1TID, <strong>pthread</strong>_kill(s1TID, SIGKILL);exit(-1);<br />
SIGKILL);exit(-1);<br />
}<br />
res res = <strong>pthread</strong>_create(&s2TID, <strong>pthread</strong>_create(&s2TID, NULL, NULL, scrittore2, scrittore2, NULL); NULL);<br />
if if (res (res != != 0) 0) {<br />
printf("Errore printf("Errore nella nella creazione creazione del del terzo terzo thread\n");<br />
thread\n");<br />
<strong>pthread</strong>_kill(lTID, <strong>pthread</strong>_kill(lTID, SIGKILL);<strong>pthread</strong>_kill(s1TID, SIGKILL);<strong>pthread</strong>_kill(s1TID, SIGKILL);<br />
SIGKILL);<br />
return return -1; -1;<br />
}<br />
/* /* Aspetto Aspetto che che i tre tre thread thread finiscano finiscano ... ... */ */<br />
<strong>pthread</strong>_join(s1TID, <strong>pthread</strong>_join(s1TID, NULL);<strong>pthread</strong>_join(s2TID, NULL);<strong>pthread</strong>_join(s2TID, NULL); NULL);<br />
<strong>pthread</strong>_join(lTID,<br />
<strong>pthread</strong>_join(lTID,<br />
NULL);<br />
NULL);<br />
printf("E' printf("E' finito finito l'esperimento l'esperimento ....\n");<br />
....\n");<br />
return return (0); (0);<br />
}<br />
Aspetti preliminari dei thread POSIX 113
Esempio 10 (5 <strong>di</strong> 6)<br />
void *scrittore1(void *in){<br />
void *scrittore1(void *in){<br />
int<br />
int<br />
i,<br />
i,<br />
j,<br />
j,<br />
k;<br />
k;<br />
for<br />
for<br />
(i=0;<br />
(i=0;<br />
i
Esempio 10 (6 <strong>di</strong> 6)<br />
void *lettore(void *in){<br />
void *lettore(void *in){<br />
int<br />
int<br />
i,<br />
i,<br />
k,<br />
k,<br />
j;<br />
j;<br />
char<br />
char<br />
local[LUN+1];<br />
local[LUN+1];<br />
local[LUN]<br />
local[LUN]<br />
=<br />
=<br />
0;<br />
0;<br />
for<br />
for<br />
(i=0;<br />
(i=0;<br />
i
Esercizio 4<br />
<strong>pthread</strong>s-4a-barrier.c<br />
mo<strong>di</strong>ficare in modo da ottenere la<br />
sincronizzazione desiderata<br />
tutti i thread si bloccano alla barriera<br />
aspettando l'arrivo <strong>di</strong> tutti gli altri<br />
tutti i thread proseguono l'esecuzione<br />
quando l'ultimo ha raggiunto la barriera<br />
suggerimento: usare una variabile<br />
con<strong>di</strong>tion<br />
<strong>pthread</strong>s-4b-barrier.c<br />
soluzione dell'esercizio precedente<br />
Aspetti preliminari dei thread POSIX 116
<strong>pthread</strong>s-4a-barrier.c<br />
(1 <strong>di</strong> 2)<br />
#include <br />
#include <br />
#include <br />
#include <br />
#include <br />
#define NUM_THREADS 4<br />
/* Some variables are needed here... */<br />
int n_threads; /* number of worker threads */<br />
/* Complete the function body */<br />
void barrier()<br />
{<br />
}<br />
void *parallel_elaboration(void * arg)<br />
{<br />
int delay = rand()%6;<br />
int i = *(int*)arg;<br />
printf("[<strong>Thread</strong> %d] Waiting for %d secs...\n",i,delay);<br />
sleep(delay);<br />
printf("[<strong>Thread</strong> %d] ...elaboration finished, <strong>wait</strong>ing for the other threads...\n",i);<br />
barrier();<br />
printf("[<strong>Thread</strong> %d] ...ok!\n",i);<br />
<strong>pthread</strong>_exit(NULL);<br />
}<br />
Continua <br />
Aspetti preliminari dei thread POSIX 117
<strong>pthread</strong>s-4a-barrier.c<br />
(2 <strong>di</strong> 2)<br />
int main(void)<br />
{<br />
<strong>pthread</strong>_t tids[NUM_THREADS];<br />
int params[NUM_THREADS];<br />
int i, rc;<br />
n_threads = NUM_THREADS;<br />
/* Some initialization goes here... */<br />
srand ( time(NULL) );<br />
printf("[Main] Starting...\n");<br />
for (i=0; i