Exercice 1 : Gestion d'une Ressource Unique
Un serveur peut traiter une seule requête à la fois. Lorsqu'une nouvelle requête arrive, elle
doit attendre que le serveur soit libre. Proposez un algorithme utilisant des sémaphores pour
gérer l'accès au serveur.
● Indication : Utilisez un sémaphore binaire pour contrôler l'accès à la ressource.
Sémaphore binaire mutex = 1
Process Requête() {
wait(mutex)
// Accès au serveur
TraiterRequête()
signal(mutex)
}
Explication :
● Le sémaphore mutex initialise à 1 (libre).
● wait(mutex) permet à une seule requête d'accéder au serveur.
● Une fois la requête traitée, signal(mutex) libère le serveur.
Exercice 2 : Synchronisation de Producteurs et Consommateurs
Dans un système de production, plusieurs producteurs déposent des éléments dans un
tampon partagé, et plusieurs consommateurs les retirent. Le tampon a une capacité limitée.
Proposez un algorithme pour synchroniser les producteurs et les consommateurs en utilisant
des sémaphores.
● Indication : Utilisez des sémaphores compteurs pour gérer le nombre d'éléments
dans le tampon et l'espace disponible.
Sémaphore compteur empty = N // Capacité du tampon
Sémaphore compteur full = 0 // Nombre d'éléments dans le tampon
Sémaphore binaire mutex = 1 // Accès au tampon
Process Producteur() {
wait(empty)
wait(mutex)
// Ajouter un élément au tampon
signal(mutex)
signal(full)
Process Consommateur() {
wait(full)
wait(mutex)
// Retirer un élément du tampon
signal(mutex)
signal(empty)
Explication :
● empty contrôle l'espace libre.
● full contrôle le nombre d'éléments présents.
● mutex protège l'accès concurrent au tampon.
Exercice 3 : Barrière de Synchronisation
Un groupe de threads doit atteindre un point de synchronisation avant de continuer leur
exécution. Une fois que tous les threads ont atteint la barrière, ils peuvent continuer.
Proposez un algorithme utilisant des sémaphores pour implémenter cette barrière.
● Indication : Utilisez un sémaphore binaire pour contrôler le passage à travers la
barrière et un compteur pour suivre le nombre de threads ayant atteint la barrière.
int count = 0
int total_threads = N
Sémaphore binaire mutex = 1
Sémaphore binaire barrier = 0
Process Thread() {
wait(mutex)
count++
if (count == total_threads) {
signal(barrier)
}
signal(mutex)
wait(barrier)
signal(barrier)
}
Explication :
● mutex protège l'incrémentation de count.
● barrier est signalé une fois que tous les threads ont atteint la barrière.
● La dernière opération signal(barrier) permet à tous les threads de passer.
Exercice 4 : Philosophe Dîneur
Cinq philosophes sont assis autour d'une table circulaire, chacun ayant une assiette de
spaghettis. Entre chaque paire de philosophes se trouve une seule fourchette. Un
philosophe ne peut manger que s'il a les deux fourchettes adjacentes. Proposez un
algorithme pour éviter les situations de famine et de blocage en utilisant des sémaphores.
● Indication : Utilisez des sémaphores binaires pour représenter les fourchettes, et
assurez-vous que l'algorithme évite les interblocages.
Sémaphore binaire fourchette[5] = {1, 1, 1, 1, 1}
Process Philosophe(int i) {
wait(fourchette[i])
wait(fourchette[(i+1) % 5])
// Manger
signal(fourchette[i])
signal(fourchette[(i+1) % 5])
}
Explication :
● Chaque philosophe prend d'abord la fourchette à sa gauche, puis à sa droite.
● Les sémaphores binaires fourchette assurent qu'une seule personne utilise une
fourchette à la fois.
● Pour éviter l’interblocage, on peut modifier l'algorithme pour qu'un philosophe prenne
toujours d'abord la fourchette la plus basse, puis la plus haute (ou inversement).
Exercice 5 : Lecture et Écriture Concurrentes
Dans une base de données, plusieurs lecteurs peuvent lire en même temps, mais l'écriture
doit être exclusive. Proposez un algorithme utilisant des sémaphores pour gérer les accès
en lecture et en écriture.
● Indication : Utilisez un sémaphore pour protéger l'accès en écriture et un compteur
pour gérer le nombre de lecteurs.
int lecteur_count = 0
Sémaphore binaire mutex = 1
Sémaphore binaire write = 1
Process Lecteur() {
wait(mutex)
lecteur_count++
if (lecteur_count == 1) {
wait(write)
}
signal(mutex)
// Lire
wait(mutex)
lecteur_count--
if (lecteur_count == 0) {
signal(write)
}
signal(mutex)
}
Process Écrivain() {
wait(write)
// Écrire
signal(write)
}
Explication :
● mutex protège l'incrémentation et la décrémentation de lecteur_count.
● write empêche les écrivains d’accéder pendant la lecture.
● Le premier lecteur bloque l'accès en écriture, et le dernier lecteur libère l'accès.