Il Blog di Fabrizio Mondo

Threads, Mutex e Semafori in C – La programmazione concorrente – Semafori

luglio 4, 2009 11:26 pm

SemaforoContinuiamo 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 thread1

Il 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 thread2

Il 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.

No Responses to “Threads, Mutex e Semafori in C – La programmazione concorrente – Semafori”

Care to comment?