Threads, Mutex e Semafori in C – La programmazione concorrente – Semafori
luglio 4, 2009 11:26 pm
Continuiamo la trattazione della programmazione concorrente presentando un altro listato recuperato su internet che possa stavolta mostrarci come funzionano e lavorano i semafori.
I semafori sono una struttura dati che permette la gestione della mutua esclusione tra processi o nel nostro caso tra threads dello stesso processo. Informazioni più complete sui semafori sono disponibili su wikipedia. Noi ci limiteremo ad esaminare del codice sorgente.
#include
#include
#include
#include/*!
\mainpage Esempio Semafori con Pthreads
\section intro Introduzione
Esempio di utilizzo semafori\date 10/05/2006
\version 23.24 04/07/2009
\author A.Dal Palu
*/static sem_t s1,s2; /// semafori
/*!
\brief Codice del thread1Il thread fa Ping e fa UP sul semaforo dell`altro thread
*/
void *thread1(void * arg)
{
int i;
for (i=0;i<10;i++){
sem_wait(&s1);
printf("Ping\n");
sem_post(&s2);
}
printf("T1 exit\n");
pthread_exit (0);
}/*!
\brief Codice del thread2Il thread fa Pong e fa UP sul semaforo dell`altro thread
*/
void *thread2(void * arg)
{
int i;
for (i=0;i<10;i++){
sem_wait(&s2);
printf("Pong\n");
sem_post(&s1);
}
printf("T2 exit\n");
pthread_exit (0);
}int main()
{
pthread_t tid1,tid2;
void * ret;sem_init(&s1,0,1);
sem_init(&s2,0,0);if (pthread_create(&tid1, NULL, thread1, NULL) < 0)
{ fprintf (stderr, "pthread_create error for thread 1\n");
exit (1);
}if (pthread_create(&tid2, NULL, thread2, NULL) < 0)
{ fprintf (stderr, "pthread_create error for thread 2\n");
exit (1);
}pthread_join (tid1, &ret);
pthread_join (tid2, &ret);printf("Exit\n");
}
Il programma è composto da tre funzioni:thread1, thread2 e il main.
Una volta inseriti gli include e inizializzato le variabili, i semafori, possiamo procedere allo studio del main.
Il main crea i thread, e una variabile d’appoggio chiamata ret, che è un puntatore a void. Dopodichè inizializza i semafori creati e i thread stessi.
Effettua anche il join dei thread e alla fine termina.
La funzione thread1, invece, effettua un altro tipo di operazioni.
La funzione sem_wait sospende il thread chiamante fintanto che il valore del semaforo puntato dall’argomento è diverso da zero. Viene inoltre decrementato automaticamente, ed atomicamente, il contatore. Questo significa che settando il valore del semaforo ad 1 ed effettuando questa chiamata il processo non si arresterà.
Successivamente viene stampato il ping e dopo parte la sem_post.
Al contrario della precedente questa funzione semplicemente incrementa il valore del semaforo passato come parametro. Qualora questo semaforo avesse già raggiunto il massimo numero consentito viene ritornato -1 mentre la variabile ERRNO viene settata ad EINVAL. In caso di successo, invece, viene restituito 0.
Esaminiamo dei possibili scenari di compilazione.
Il codice per come è scritto se viene compilato ed eseguito, restituisce un output del genere:
Ping
Pong
Ping
Pong
Ping
Pong
Ping
Pong
Ping
Pong
…
T1 exit
Pong
T2 exit
Exit
Questo perchè? Perchè il primo dei due semafori ha come valore iniziale 1. Se si modificano i valori reciproci dei semafori, la situazione cambia non poco.
Ricordiamo intanto quali sono le operazioni che vengono fatte dalle funzioni. All’interno della funzione thread1, che è la prima ad essere invocata, la sem wait diminuisce il valore del semaforo, che non è 0, e per questo il thread non si blocca. Esegue quello che deve eseguire, e invoca la sem post sull’altro thread, che passa da 0 (valore iniziale) a 1.
Il gioco si ripete per il thread 2, proprio come il ping pong
Cosa succede nei casi in cui:
1) I due semafori partono entrambi da 0
2) Il primo parte da 0 e il secondo da 1
3) Partono entrambi da 1
4) Il primo ha un valore molto superiore al secondo
5) Entrambi partono da N
Esaminiamo caso per caso, prima logicamente, poi compilando e vedendo se avevamo ragione o meno…
Se T1 e T2 partono da 0 cosa succede? Viene sempre invocata thread1 per prima, che invoca la wait che incontra un semaforo con valore 0 e blocca il thread. Il quanto finisce e viene fatto lavorare il thread2, che non può non avere lo stesso destino perchè anche lui parte da 0, morale della favola: Deadlock!
Se T1 parte da 0 e T2 parte da 1 cosa succede? Semplicemente il flusso si inverte, e si parte con Pong, piuttosto che con Ping.
Pong
Ping
Pong
Ping
Pong
Ping
Pong
Ping
Pong
Ping
…
T1 exit
T2 exit
Exit
Questo perchè PRIMA viene eseguita una wait su un semaforo a valore 0.
Se T1 parte da 1 e T2 parte da 1 cosa succede? Non cambia nulla rispetto al partire t1 con 0 e t2 con 0, in quanto comunque t1 avrebbe incrementato t2 prima di terminare la sua esecuzione per cui alla fine, il comportamento è lo stesso.
Se t1 è molto maggiore di t2 in valore iniziale? Semplicemente si vedranno tanti PING consecutivi in base al numero di partenza del semaforo, perchè il ciclo chiamerà sem_wait decrementando un numero che non diventerà 0 prima delle N volte impostate inizialmente. Dopo il ciclo continuerà con un ping pong, fintantochè non si esaurisce il ciclo del primo thread, che muore, e ci saranno da allora solo pong.
Se t1 e t2 partono da N cosa succede? Caso da considerarsi identico al precedente.
Categories: Articoli didattici.. o quasi!
No Comments »
