Cours : Système d’exploitation
Chapitre 4 : Threads & Sémaphores
Enseignante: Mme BEN ABDELJELIL Mouna
A.U: 2021/2022
PLAN
PARTIE 1 : LES THREADS
PARTIE 2 : LES SÉMAPHORES
2
LES THREADS
3
Définitions
1. Processus
- Chaque application (emacs, Word, etc.) exécutée sur un ordinateur lui est associée un processus représentant l'activité de
celle-ci.
- À ce processus est associé un ensemble de ressources personnalisées comme l'espace mémoire, le temps CPU etc.
- Ces ressources seront dédiées à l'exécution des instructions du programme associé à l'application.
- Les processus coûtent cher au lancement (l'espace mémoire à calculer, les variables locales au processus à définir, etc.)
2. Multitâche
C'est la possibilité que le système d'exploitation a pour faire fonctionner plusieurs programmes en même
temps et cela, sans conflit. -
Par exemple: écouter de la musique et lire son courrier en même temps.
- Un système monoprocesseur (et non pas mono-processus, ne pas confondre processus et processeur),
simule le multitâche en attribuant un temps (un quantum ou temps déterminé à l'avance) de traitement
pour chaque processus (description très simpliste du rôle du monoprocesseur).
4
Les threads
- Un système multiprocesseur peut exécuter les différents processus sur les différents processeurs du système.
- Généralement, un programme est livré avec un tel système pour permettre de distribuer de manière efficace les processus
sur les processeurs de la machine.
- Un programme est dit multitâche s'il est capable de lancer plusieurs parties de son code en même temps.
- À chaque partie du code sera associé un processus (sous-processus) pour permettre l'exécution en parallèle.
- La possibilité de lire l'aide en ligne et d'imprimer à la fois sous Word. Ce sont deux sous-processus relevant d'un processus
qui est associé à l'application Word.
- Un serveur réseau (processus) et le traitement de requêtes (sous-processus associé à chaque requête).
- Nous aimerions que le traitement de ces requêtes soit fait de manière optimale de telle sorte à pouvoir servir toutes les
requêtes dans un espace de temps raisonnable.
- Comme chaque processus coûte cher au lancement, sans oublier qu'il va avoir son propre espace mémoire, la
commutation (passage d'un processus à un autre) ainsi que la communication interprocessus (échange d'informations)
seront lourdes à gérer.
5
Définition : Les threads
Question
- Peut-on avoir un sous-processus qui permettrait de lancer une partie du code d'une application sans qu'il soit onéreux
- La réponse est venue avec les threads qui sont appelés aussi « processus légers ».
3. Threads
Un thread est un sous-ensemble d'un processus, partageant son espace mémoire et ses variables.
De ce fait, les coûts associés suite à son lancement sont réduits, donc il est plus rapide.
En plus de cela, à chaque thread est associé des unités propres à lui: comme sa pile (pour gérer les instructions à exécuter par le thread),
le masque de signaux (les signaux que le thread doit répondre), sa priorité d'exécution (dans la file d'attente), des données privées, etc.
Il faut ajouter à cela, les threads peuvent être exécutés en parallèle par un système multitâche.
Cependant, le partage de la mémoire et les variables du processus entraînent un certain nombre de problèmes lorsqu'il y a un accès
partagé à une ressource, par exemple.
Deux agents (threads) voulant effectuer des réservations de places d'avion (ressource: nombre de places disponibles dans un avion).
6
Les threads : Création de thread
Identifier les threads I
pthread_t : équivalent du pid_t (c’est une structure opaque) I
pthread_t pthread_self () : identité du thread courant
int pthread_equal (pthread t, pthread t) : test d’égalité Créer de nouveaux threads I
Le programme est démarré avec un seul thread, les autres sont créés à la main I
int pthread_create(identite, attributs, fonction, argument) ;
identite : [pthread_t*] identifieur unique du nouveau thread (rempli par l’appel)
attributs : Pour modifier les attributs du thread (NULL → attributs par défaut)
fonction : [void *(*)(void *)] Fonction C à exécuter (le main du thread)
argument : argument à passer à la fonction. Doit être transtypé en void*
retour : 0 si succès,
errno en cas de problème
7
Les threads : jointure de thread
Joindre un thread
int pthread_join ( pthread_t, void ** )
Le thread A joint le thread B : A bloque jusqu’’à la fin de B.
Utile pour la synchronisation (rendez-vous) I
Second argument reçoit un pointeur vers la valeur retour du pthread_exit() I
Similaire au wait() après fork() (mais pas besoin d’être le créateur pour joindre un thread)
retour : 0 si succès,
errno en cas de problème
8
Les threads Schémas d’implémentation
Avantages et inconvénients : plus rapide, mais plus dangereux I
Schémas d’implémentations :
Modèle M:1 : M threads par processus (portable et rapide, pas parallèle)
Modèle 1:1 : Threads gérées par l’OS directement (plus dur à implémenter, plus efficace en pratique) I
Modèle M:N : Gestion conjointe (modèle théorique meilleur, difficile à faire efficacement) I
Les fonctions principales de l’interface POSIX :
pthread_create : crée un nouveau thread
pthread_exit : termine le thread courant
pthread_join : attend la fin du thread et r´ecup`ere son errcode I
pthread_detach : indique que personne ne rejoindra ce thread I mutex {lock,unlock,*}:
usage des mutex
sem {wait,post,*}: usage des sémaphore
9
cond {wait,signal,*}: usage des conditions
Utilisation de threads
Exercice d’application : #include <pthread.h>
#include <stdio.h>
Écrire un programme C qui permet de saisir deux int a,b,s,p;
variables entières a et b, calculer leur somme, float q;
void *somme(void *arg) {
leur produit et leur quotient chacun dans un s=a+b;
thread, puis afficher les résultats. }
void *produit(void *arg) {
p=a*b;
}
void *division(void *arg) {
if(b!=0)
q=(float)a/b;
}
void main() {
pthread_t tid1,tid2,tid3;
printf("Donner le premier nombre : ");
scanf("%d",&a);
printf("Donner le deuxieme nombre : ");
scanf("%d",&b);
pthread_create(&tid1, NULL, somme, NULL) ;
pthread_create(&tid2, NULL, produit, NULL) ;
pthread_create(&tid3, NULL, division, NULL) ;
pthread_join(tid1, NULL) ;
pthread_join(tid2, NULL) ;
pthread_join(tid3, NULL) ;
printf("somme s=%d\n",s);
printf("produit p=%d\n",p);
printf("division q=%.2f\n",q);
} 10
Problème de Section Critique
Exp2 :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
long solde = 400;
int n=5;
void *DebiterSolde(void *arg)
{
solde-=40;
Section critique
printf("Solde debite =%ld\n",solde);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t t[n];
for (int i = 0; i < n; ++i) {
pthread_create(&t[i], NULL, DebiterSolde, NULL);
}
for (int i = 0; i < n; ++i) {
pthread_join(t[i], NULL);
}
Section critique :(if u use a variable object its called by force section critique)(its access includind modification)
printf("solde final = %ld\n", solde);
exit(EXIT_SUCCESS);
11
}
Problème d’Accès à des variables partagées
Accès concurent à une mémoire partagée peut produire des incohérences
Maintenir la cohérence des données requière des mécanismes pour assurer
une exécution correcte des processus coopérants
Une solution mémoire partagée au problème de tampon borné (bounded
buffer) contient un problème de “race condition” (condition de concurrence)
sur la variable donnée solde.
12
Sections critiques(SC): Définition
• Section Critique = ensemble de suites
d’instructions qui peuvent produire des résultats
imprévisibles lorsqu’elles sont exécutées
simultanément par des processus différents.
13
Exclusion Mutuelle: Principe(1)
Les SC doivent être exécutés en Exclusion Mutuelle:
Avant d’exécuter une SC, un processus doit s’assurer qu’aucun autre
processus n’est en train d’exécuter une SC du même ensemble.
Dans le cas contraire, il devra pas progresser, tant que l’autre
processus n’aura pas terminé sa SC;
14
Exclusion Mutuelle: Principe(2)
Nécessité de définir un protocole d’entrée en SC et un protocole de sortie de SC.
15
Protocole d ’entrée/sortie en SC
• SE : Section d’Entrée : Protocole d ’entrée en SC:
ensemble d ’instructions qui permet cette vérification
et la non progression éventuelle;
• SS : Section de Sortie : Protocole de sortie de SC:
ensemble d’instructions qui permet à un processus
ayant terminé sa SC d ’avertir d ’autres processus
en attente que la voie est libre
16
Structure des processus
Début
Section non Critique
protocole d ’entrée
SC
protocole de sortie
Section non critique
Fin.
17
Solutions proposées
Variable partagée
Algorithmes de Dekker
Algorithmes de Peterson
Les sémaphores
18
LES SÉMAPHORES
19
Les sémaphores : Historique & Définition
• Introduit par Dijkstra en 1965 pour résoudre le problème d’exclusion
mutuelle.
• Permettent l’utilisation de m ressources identiques (exp : imprimantes)
par n processus.
Un sémaphore est une variable globale protégée, c’est à dire on peut y
accéder qu’au moyen des trois procédures :
I(S, x) : initialiser le sémaphore S à une certaine valeur x;
P(S) ; Peut -on passer ?/peut-on continuer?
V(S) ; libérer?/vas y?
20
Les sémaphores : Définition
Un sémaphore est une structure contenant deux champs :
struct semaphore
{
n : entier ;
en_attente : file de processus
}
P(S) ; peut -on passer ?
V(S) ; libérer?
21
Sémaphores : Utilisation dans le code
22
Sémaphores: Réalisations (programmation C)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
23
Sémaphores: Réalisations logicielles
Initialisation #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
Une fois l'objet créé, on peut l'initialiser:
#include <semaphore.h>
int sem_init(sem_t *semaphore, int pshared, unsigned int valeur);
semaphore → pointeur vers le sémaphore à initialiser ;
pshared → drapeau qui précise si le sémaphore est utilisé par des threads (valeur 0) ;
valeur → valeur de départ du sémaphore.
le code retour varie entre:
'0' si tout s'est bien passé
'1' si une erreur survient et errno est positionné
24
Sémaphores: Réalisations logicielles
Blocage #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
Une fois le sémaphore initialisé, on peut demander son blocage avec
#include <semaphore.h>
int sem_wait(sem_t *semaphore);
semaphore → pointeur vers le sémaphore à bloquer ;
le code retour varie entre:
'0' si tout s'est bien passé
'-1' si une erreur survient et errno est positionné
int sem_wait(semaphore *s)
{
s→val = s→val-1;
if(s→val<0)
{
// Place this thread in s→queue;
// This thread is blocked;
}
} 25
Sémaphores: Réalisations logicielles
Relâchement d’un sémaphore #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
Une fois la section critique passée, on peut relâcher le sémaphore:
#include <semaphore.h>
int sem_post(sem_t *semaphore);
semaphore → pointeur vers le sémaphore bloqué ;
le code retour varie entre:
'0' si tout s'est bien passé
'-1' si une erreur survient et errno est positionné
int sem_post(semaphore *s)
{
s→val = s→val+1;
if(s→val<=0)
{
// Remove one thread(T) from s→queue;
// Mark Thread(T) as ready to run;
}
}
26
Sémaphores: Réalisations logicielles
Destruction #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *semaphore);
semaphore → pointeur vers le sémaphore à détruire;
le code retour varie entre:
'0' si tout s'est bien passé
'-1' si une erreur survient et errno est positionné
27
Utilisation des sémaphores
#include <stdio.h> Exercice d’application : protection de la section critique
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
static long solde = 400;
static int n=5;
static sem_t mutex;
static void *DebiterSolde(void *arg)
{
sem_wait(&mutex) ;
solde-=40;
printf("Solde debite =%ld\n",solde);
sem_post(&mutex);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t t[n];
sem_init(&mutex, 0, 1);
for (int i = 0; i < n; ++i) {
pthread_create(&t[i], NULL, DebiterSolde, NULL);
}
for (int i = 0; i < n; ++i) {
pthread_join(t[i], NULL);
}
printf("solde final = %ld\n", solde);
exit(EXIT_SUCCESS);
}
28
Propriétés de l’exclusion Mutuelle
1. Un seul processus en SC;
2. Un processus qui veut entrer en SC ne doit pas attendre
qu’un autre processus passe avant lui pour avoir le droit.
3. Un processus désirant entrer en SC y entre au bout d’un
temps fini; pas de privation d’y entrer vis à vis d’un
processus
29
Autres propriétés de l ’exclusion Mutuelle
• La tolérance aux défaillances: si le processus en SC est
détruit ou se termine anormalement, il ne faut pas qu’il
bloque tout le système;
• La symétrie: Les protocoles d’E/S en SC doivent être
identiques pour tous les processus et indépendants de
leur nombre.
30
Exclusion Mutuelle
• L ’exclusion Mutuelle n’est pas garantie si:
– a) un processus peut entrer en SC alors qu’un
autre s’y trouve déjà;
– b) un processus désirant entrer en SC ne peut pas
y entrer alors qu’il n’y a aucun processus en SC;
– c) un processus désirant entrer en SC n ’y entrera
jamais car il sera jamais sélectionné lorsqu’il est
en concurrence avec d ’autres processus
31
Solution au Problème de Section Critique
1. Exclusion Mutuelle – Si le processus Pi est dans sa section critique,
alors aucun autre processus ne peut exécuter sa section critique
2. Progression – S’il n’y a aucun processus dans la section critique et
que des processus veulent entrer dans leur section critique, alors la
sélection des processus pour entrer en section critique ne peut pas
être retardée indéfininement
3. Attente Bornée - Une borne doit exister sur le nombre de fois que
les autres processus sont permis d’entrer dans leur section critique
après qu’un processus ait fait une requête d’admission en section
critique et avant que cette demande ne soit accordée
Supposer que chaque processus exécute à une vitesse
différente de 0
Pas d’hypothèses concernant les vitesses d’exécution relatives
des processus
32
Sémaphores: Types de Sémaphore
Un sémaphore binaire est un sémaphore dont la valeur ne peut prendre que deux
valeurs possibles : 1 et 0.
* Un sémaphore binaire est un sémaphore qui est initialisé avec la valeur 1. Ceci a pour
effet de contrôler l’accès une ressource unique.
* Le sémaphore binaire permet l’exclusion mutuelle (mutex) : Une ressource est en
exclusion mutuelle si seul un processus peut utiliser la ressource à un instant donné.
* Un sémaphore qui est initialisé avec la valeur 0 est un sémaphore bloquant.
Un sémaphore de comptage : la valeur peut prendre plus de deux valeurs positives
possibles.
Il est utile pour allouer une ressource parmi plusieurs exemplaires identiques : la valeur est
initialisée avec le nombre de ressources.
33
34
Sémaphore Binaire Mutex
sem_t Mutex;
sem_init(&Mutex, PTHREAD_PROCESS_SHARED, 1);
sem_wait(&Mutex);
sem_post(&Mutex);
sem_destroy(&Mutex);
35
Utilisation de sémaphore binaire
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main() {
#include <sys/wait.h> // Création d'un tableau de thread
#include <string.h> pthread_t threads[NB_THREAD];
#include <pthread.h> // Initialisation du sémaphore
#include <errno.h> sem_init(&Mutex, PTHREAD_PROCESS_SHARED, 1);
#include <semaphore.h> for (int i = 0; i < NB_THREAD; i++) {
#define NB_THREAD 4 int err;
#define LIMIT 2 if ((err = pthread_create(&threads[i], NULL, job,
NULL)) != 0) {
// Création du sémaphore; printf("Echec de la création du thread:
sem_t Mutex; [%s]", strerror(err));
return EXIT_FAILURE;;
void * job(void * args) { }
// Récupération de l'identifiant du thread printf("Création du thread numéro %i\n", i);
int tid = pthread_self(); }
int i = 0; for (int i = 0; i < NB_THREAD; i++) {
while (i < LIMIT) { pthread_join(threads[i], NULL);
// On attend la disponibilité du sémaphore }
sem_wait(&Mutex); sem_destroy(&Mutex);
// Section critique return EXIT_SUCCESS;
}
printf("Je suis le thread [%i] et je vais dormir 1 seconde\n", tid);
sleep(1);
printf("Je suis le thread [%i] et j'ai fini ma sieste\n", tid);
// On relache le sémaphore
sem_post(&Mutex);
i++;
}
pthread_exit(EXIT_SUCCESS);
}
36
Sémaphore Binaire Bloquant
Problème de rendez-vous
37
Rendez vous à N processus
Soient N processus parallèles ayant un point de rendez-vous. sémaphore mutex = 1, semaphore= 0;
Un processus arrivant au point de rendez-vous se met en attente s'il existe entier NbArrivés = 0; /*nbre de processus arrivés au rendez-vous*/
au moins un autre processus qui n'y est pas arrivé.
Procédure RDV
Début
sem_wait(mutex);
NbArrivés = NbArrivés + 1;
Si (NBArrivés < N) Alors /* non tous arrivés */
Le dernier arrivé réveillera les processus bloqués. sem_post(mutex); /* on libère mutex et */
sem_wait(semaphore); /* on se bloque */
Sinon
sem_post(mutex); /* le dernier arrivé libère mutex et */
Pour i = 1 à N-1 Faire
sem_post(semaphore); /* réveille les N-1 bloqués */
Finsi
Fin
38
Sémaphores
Problème de Barbier
Un coiffeur dispose dans son salon d'un fauteuil de coiffure et de N chaises pour les
clients en attente. S'il n'y a pas de clients, le coiffeur dort.
À l'arrivée du premier client, il se réveille et s'occupe de lui. Si un nouveau client arrive
pendant ce temps, il attend sur l'une des chaises disponibles. Mais si toutes les chaises
sont occupées, le client s'en va.
Enfin lorsque le coiffeur a fini de couper les cheveux du dernier client, il peut se rendormir.
Indications :
• utiliser un sémaphore pour gérer les clients en attente, et un autre indiquant si le coiffeur
est disponible.
• ajouter une variable indiquant le nombre de clients en attente. 39
clients = 0 ; processus client
Barbiers = 0 ; début
mutex = 1 ; P(mutex);
poireau = 0 ; Si poireau < CHAISES alors
processus barbier
//rester
début
poireau = poireau + 1 ;
Répéter indéfiniment
V(clients) ;
P(clients) ;
V(mutex) ;
//si pas de clients dormir
P(barbiers) ;
P(mutex) ;
se_faire_raser() ;
poireau = poireau - 1 ;
sinon
V(barbiers) ;
V(mutex);
V(mutex) ;
//partir
Raser() ;
FinSi
FinRépéter
fin
40
fin
Sémaphore de comptage
41
Applications avec des Sémaphores
Exemples de problèmes :
Problème de philosophes.
Problème des rédacteurs et des
lecteurs
Problème des producteurs et des consommateurs
42
Sémaphores
Problème de philosophes
Considérons cinq philosophes, installés autour d'une table circulaire, et qui passent leurs temps à penser et à manger.
NB : le nombre des philosophes peut être quelconque, mais il doit être au moins égal à cinq pour garantir le bon fonctionnement du
programme.
43
Problème de philosophes
// penser
#include<stdio.h> sleep ( 1 ) ;
#include<unistd.h> // tenter de manger
#include<pthread.h> sem_wait(&mutex ) ;
#include<semaphore.h> Etat [i]= faim ;
Test (i) ;
sem_post (&mutex ) ;
#define N 5 sem_wait(&S[i] ) ;
#define G ((N+i-1)%N) // philosophe de gauche de i // manger
#define D (i) // philosophe de d r o i t e de i printf ("philosophe [%d ] mange\n" , i ) ;
enum etat{penser,faim,manger}; sleep ( 1 ) ; // manger
enum etat Etat[N]={penser,penser,penser,penser,penser}; printf ("philosophe [%d ] a fini de manger\n" , i ) ;
sem_t S[N]; // libérer les fourchettes
sem_t mutex ; sem_wait(&mutex ) ;
void* philosophe ( void* ); Etat [ i ] = penser ;
void Test ( int i ) ; // vérifier si ses voisins peuvent manger
int main () Test (G ) ;
{ Test (D ) ;
int i , NumPhi [N ] = { 0 , 1 , 2 , 3 , 4 } ; sem_post (&mutex ) ;
pthread_t ph [N] ; }
sem_init (&mutex , 0 , 1 ) ; }
for( i = 0 ; i <N ; i ++)
sem_init(&S [ i ] , 0 , 0 ) ; // procedure qui vérifie s il le philosophe i peut manger
// déclaration des N philosophes void Test ( int i )
for( i = 0 ; i <N ; i ++) {
pthread_create (&ph [i] , NULL, philosophe , & (NumPhi [i] ) ) ; if ( ( Etat [ i ] == faim ) && ( Etat [G ] != manger )
// attendre la fin des threads && ( Etat [D] != manger ) )
for(i = 0 ; ( i <N && pthread_join ( ph [ i ] , NULL) == 0 ) ; i++) ; {
printf("fin des threads \n") ; Etat[i] = manger ;
return 0 ; sem_post (&S [i] ) ;
} }
void * philosophe ( void* num) }
{
int i =* (int*) num ;
int nb = 2 ;
while ( nb )
{ 44
nb--;
Sémaphores
Exemples : Problème des rédacteurs et des lecteurs
45
Problème de lecteur/rédacteur
#include<stdio.h> // attendre l a fin des threads void* redacteur (void* num)
#include<unistd.h> for ( i = 0 ; i <N ; i ++) {
#include<pthread.h> { int i =* ( int *) num ;
#include<semaphore.h> pthread_join ( red [ i ] , NULL) ; int x=ACCES ;
pthread_join ( lec [ i ] , NULL) ; do
} {
#define N 3 printf ( "fin des threads \n " ) ; printf ( " Redacteur %d\n " , i ) ;
#define ACCES 4 return 0 ; sem_wait(& Redact ) ;
} // modifier les données de la base
int NbL = 0 ; sleep ( 1 ) ;
sem_t Redact ; sem_post(& Redact ) ;
sem_t mutex ; void *lecteur ( void* num) } while(--x);
void * lecteur ( void* ) ; { }
void* redacteur ( void* ) ; int i =* ( int*) num ;
int x=ACCES ;
int main () do
{ {
int i ; printf ( " L e c t e u r %d\n " , i ) ;
int numred [N ] = { 0 , 1 , 2 } ; sem_wait(&mutex ) ;
int numlec [N ] = { 0 , 1 , 2 } ; if ( ! NbL)
pthread_t red [N] ; sem_wait(& Redact ) ;
pthread_t lec [N] ; NbL++;
sem_init(&mutex , 0 , 1 ) ; sem_post(&mutex ) ;
sem_init(& Redact , 0 , 1 ) ; //lecture de la base
for ( i = 0 ; i <N ; i ++) sleep ( 1 ) ;
{ // fin de l’accès à la base
// création des redacteurs sem_wait(&mutex ) ;
pthread_create(& red [ i ] , NULL, lecteur , & ( numred[ i ] ) ) ; NbL--;
// creation des lecteurs if ( ! NbL)
pthread_create(& lec [ i ] , NULL, redacteur , & ( numlec [ i ] ) ) ; sem_post (& Redact ) ;
// creation des lecteurs sem_post(&mutex ) ;
pthread_create (&lec [ i ] , NULL, redacteur , & ( numlec [ i ] ) ) ; // traitement des donneés lues
} sleep ( 1 ) ;
} while(--x);
}
46
Sémaphores
Problème des producteurs et des consommateurs
47
Problème de producteur / consommateur
include <stdlib.h> /* Consommer un objet */
#include <stdio.h> int consommation(int *item) {
#include <pthread.h> if(counter > 0) {
#include <semaphore.h> *item = buffer[(counter-1)];
#define RAND_DIVISOR 10000000 counter--;
#define TRUE 1 printf("Consommateur %u a consommé %d\n", pthread_self(),*item);
#define BUFFER_SIZE 5 }
else
sem_t full, empty; /* Les semaphores */ printf("Consommateur signale une erreur !!\n");
int buffer[BUFFER_SIZE]; /* Le buffer (tampon) */ }
int counter; /* Compteur du buffer */ /* Thread Producteur */
void *producer(void *param) {
int item;
pthread_t tid; while(TRUE) {
pthread_attr_t attr; /* Thread ID*/ /* dormir un temps aléatoire */
/* Attribut de la Thread */ int rNum = rand() / RAND_DIVISOR;
void initializeData() { usleep(rNum);
/* Créer un sémaphore full initialisé à 0 */ /* generer un objet (nombre aléatoire) */
sem_init(&full, 0, 0); item = rand();
/* Créer un sémaphore empty initialisé à BUFFER_SIZE */ sem_wait(&empty);
sem_init(&empty, 0, BUFFER_SIZE); production(item);
/* Préparer les atributs par défaut pour la thread */ sem_post(&full);
pthread_attr_init(&attr); }
}
/* initialisation du compteur */
counter = 0;
}
/* Produire un objet */
int production(int item) {
if(counter < BUFFER_SIZE) {
buffer[counter] = item;
counter++;
printf("Producteur %u a produit %d\n", pthread_self(),item);
}
else
printf("Producteur signale une erreur !!\n"); 48
}
Problème de producteur / consommateur
/* Thread Consommateur */
void *consumer(void *param) {
int item;
while(TRUE) {
/* dormir un temps aléatoire */
int rNum = rand() / RAND_DIVISOR;
usleep(rNum);
sem_wait(&full);
consommation(&item);
sem_post(&empty);
}
}
int main(int argc, char *argv[]) {
int i;
/* Verifier que le nombre d'arguments passé au programme est correct */
if(argc != 4) {
fprintf(stderr, "USAGE:./producer_consumer <ARG1> <ARG2> <ARG3>\n");
}
/*Lire les arguments dans argv[] Note: argv[0] contient le nom du programme lui-même*/
int mainSleepTime = atoi(argv[1]); /* Temps en microseconde pour le sommeil du main */
int numProd = atoi(argv[2]); /* Nombre de threads producteurs */
int numCons = atoi(argv[3]); /* Nombre de threads consommateurs */
/* Initialisation */
initializeData();
for(i = 0; i < numProd; i++) {
pthread_create(&tid,&attr,producer,NULL);
}
for(i = 0; i < numCons; i++) {
pthread_create(&tid,&attr,consumer,NULL);
}
usleep(mainSleepTime);
printf("Fin du programme\n");
exit(0);
}
49
Questions ?
50