Thread POSIX

Transcript

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