Systèmes d’exploitation & Programmation Concurrente II2
Ce TP porte sur la synchronisation de threads dans un contexte de programmation concurrente. Il propose deux exercices pratiques : le premier modélise la gestion d’une salle de TP avec un nombre limité d’étudiants pouvant y accéder simultanément, et le second simule la gestion concurrente d’un compte bancaire avec des dépôts et retraits effectués par plusieurs threads.
D'après le document Systèmes d’exploitation & Programmation Concurrente II2
Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source
Programming, Multithreading, Synchronization · DOCX · 6 pages · 2014
Ce TP porte sur la synchronisation de threads dans un contexte de programmation concurrente. Il propose deux exercices pratiques : le premier modélise la gestion d’une salle de TP avec un nombre limité d’étudiants pouvant y accéder simultanément, et le second simule la gestion concurrente d’un compte bancaire avec des dépôts et retraits effectués par plusieurs threads. Ce TP permet d’apprendre à utiliser les sémaphores pour synchroniser des threads et éviter les problèmes classiques de concurrence. Pour réaliser ce TP, il faut disposer d’un environnement de programmation supportant les threads POSIX et les sémaphores.
Objectifs
- Comprendre et appliquer la synchronisation de threads à l’aide de sémaphores.
- Modéliser un problème réel (gestion d’une salle de TP) avec des threads et des sémaphores.
- Compléter un code multithreadé en respectant des contraintes de synchronisation.
- Simuler la gestion concurrente d’un compte bancaire avec des opérations de dépôt et retrait.
- Mettre en œuvre l’exclusion mutuelle et la synchronisation conditionnelle entre threads.
Prérequis et installation
- Connaissances de base en programmation C et en programmation concurrente.
- Compréhension des concepts de threads POSIX (pthread) et des sémaphores (sem_t).
- Environnement de développement supportant pthread et semaphores POSIX (Linux, macOS, etc.).
- Fonctions externes supposées définies : ouvrir_porte(), fermer_porte(), enseigner().
Exercice 1 : Synchronisation d’accès à une salle de TP
Dans cet exercice, on modélise un enseignant qui organise plusieurs séances de rattrapage dans une salle de TP pouvant accueillir 10 étudiants à la fois. Le total d’étudiants est 80, donc la séance doit être répétée plusieurs fois. L’enseignant ouvre la porte, attend que la salle soit pleine, ferme la porte, enseigne, puis ouvre la porte pour laisser sortir les étudiants. Ce processus se répète jusqu’à ce que tous les étudiants aient assisté à la séance.
Procédure
Le programme crée un thread enseignant et 80 threads étudiants. Le thread enseignant est déjà implémenté et utilise plusieurs sémaphores pour contrôler l’entrée et la sortie des étudiants. La tâche consiste à compléter la fonction thread_etudiant() en respectant les contraintes suivantes :
- Ne pas déclarer de nouvelles variables.
- Ne pas modifier la structure de base de la fonction.
- Utiliser les variables et sémaphores déjà déclarés.
Voici le squelette de la fonction à compléter :
void *thread_etudiant(void *arg)
{
/* entrer dans la salle */
etudiants_entrant += 1;
if (etudiants_entrant == CLASS) {
etudiants_entrant = 0;
}
/* Quitter la salle */
etudiants_sortant += 1;
if (etudiants_sortant == CLASS) {
etudiants_sortant = 0;
}
return NULL;
}
Complétion attendue
Pour synchroniser correctement les entrées et sorties, il faut utiliser les sémaphores pour :
- Attendre que l’enseignant ouvre la porte avant d’entrer.
- Signaler à l’enseignant lorsque la salle est pleine.
- Attendre que l’enseignant ouvre la porte pour sortir.
- Signaler à l’enseignant lorsque tous les étudiants sont sortis.
Le code complet de la fonction thread_etudiant() doit donc inclure les opérations P (attente) et V (signal) sur les sémaphores appropriés, ainsi que la protection des variables partagées avec les mutex. Voici une version corrigée :
void *thread_etudiant(void *arg)
{
/* attendre l'autorisation d'entrer */
P(&entrer_file);
/* protéger la section critique */
P(&mutex2);
etudiants_entrant += 1;
if (etudiants_entrant == CLASS) {
etudiants_entrant = 0;
V(&tous_entres); /* signaler que la salle est pleine */
}
V(&mutex2);
/* attendre que l'enseignant ferme la porte et enseigne */
/* (pas d'action spécifique ici, le thread reste dans la salle) */
/* attendre l'autorisation de sortir */
P(&sortir_file);
/* protéger la section critique */
P(&mutex3);
etudiants_sortant += 1;
if (etudiants_sortant == CLASS) {
etudiants_sortant = 0;
V(&tous_sortis); /* signaler que tous sont sortis */
}
V(&mutex3);
return NULL;
}
Exercice 2 : Gestion d’un compte bancaire concurrent
Ce second exercice simule un compte bancaire partagé entre plusieurs threads effectuant des dépôts et des retraits. La synchronisation est nécessaire pour :
- Garantir l’exclusion mutuelle lors des mises à jour du solde.
- Bloquer les retraits si le solde est insuffisant.
- Signaler aux threads de retrait lorsqu’un dépôt a été effectué.
Consignes principales
- Les dépôts sont aléatoires entre 1 $ et 200 $.
- Les retraits sont aléatoires entre 1 $ et 50 $.
- Trois threads de dépôt et quatre threads de retrait s’exécutent simultanément.
- Après un dépôt, le thread dort environ 1 milliseconde pour laisser la place aux autres threads.
- Les threads de retrait cèdent le CPU après chaque opération pour éviter la monopolisation.
- Les threads de retrait bloquent s’ils tentent de retirer plus que le solde disponible.
- Tous les threads ont la même priorité.
- Utiliser les mécanismes de synchronisation adéquats (mutex, variables conditionnelles ou sémaphores) pour gérer l’exclusion mutuelle et la coordination.
Déroulement attendu
Chaque thread de dépôt effectue un dépôt aléatoire, met à jour le solde, puis signale les threads de retrait en attente. Chaque thread de retrait tente un retrait aléatoire, bloque si le solde est insuffisant, puis cède le CPU. Le programme affiche les opérations effectuées, illustrant la synchronisation entre threads.
Résultats attendus
- Pour l’exercice 1, la salle doit toujours contenir exactement 10 étudiants pendant la séance, sans dépassement ni sous-effectif.
- L’enseignant ne commence à enseigner qu’une fois la salle pleine.
- Les étudiants sortent tous avant que la porte ne soit réouverte pour la séance suivante.
- Pour l’exercice 2, le solde du compte ne doit jamais devenir négatif.
- Les retraits bloquent correctement quand le solde est insuffisant et reprennent après un dépôt.
- Les opérations de dépôt et retrait s’enchaînent sans interblocage ni perte de mise à jour.
- La sortie du programme doit refléter les opérations dans un ordre cohérent et synchronisé.
Pièges courants
- Ne pas protéger les variables partagées (etudiants_entrant, etudiants_sortant, solde) avec des mutex, ce qui peut causer des conditions de course.
- Oublier de signaler les sémaphores ou variables conditionnelles, bloquant ainsi les threads indéfiniment.
- Permettre à plus de 10 étudiants d’entrer dans la salle simultanément.
- Ne pas gérer correctement la remise à zéro des compteurs d’étudiants entrant et sortant.
- Dans l’exercice 2, ne pas bloquer les retraits lorsque le solde est insuffisant, ce qui peut provoquer un solde négatif.
- Ne pas céder le CPU dans les threads de retrait, ce qui peut entraîner une monopolisation du processeur.
- Ne pas respecter les temps de sommeil dans les threads de dépôt, empêchant une bonne alternance des opérations.
Commentaires
Aucun commentaire pour le moment. Posez la première question.