Philosophers
philosophers est un projet concurrence / synchronisation en C basé sur le problème classique des “Dining Philosophers”. L’objectif est de gérer plusieurs threads/processus qui partagent des ressources (les fourchettes) sans tomber dans les pièges habituels : deadlocks, race conditions et incohérences de logs.
L’objectif est double :
- Comprendre et maîtriser la programmation concurrente (threads/processus) et la synchronisation.
- Construire une simulation robuste : règles de temps respectées, arrêt propre, affichage cohérent.
Principe
Le programme simule n philosophes assis autour d’une table, qui alternent :
- prendre des fourchettes (ressources partagées),
- manger pendant
time_to_eat, - dormir pendant
time_to_sleep, - penser.
Contrainte critique : si un philosophe ne mange pas dans time_to_die, il meurt et la simulation s’arrête (selon les règles du sujet).
Selon la version :
- mandatory : threads + mutex (fourchettes et affichage),
- bonus : processus + sémaphores (IPC).
Points importants
-
Synchronisation :
-
protection des fourchettes (mutex/semaphore),
- protection des logs (éviter des messages mélangés),
- protection des états partagés (dernier repas, compteur de repas, flag stop…).
-
Éviter les deadlocks :
-
stratégie d’ordre de prise des fourchettes (pair/impair ou hiérarchie),
- éventuellement limitation via un “waiter” / sémaphore global.
-
Monitoring :
-
un thread/processus surveille les timestamps pour détecter une mort,
- arrêt propre de tous les workers dès qu’une condition de fin est atteinte.
-
Précision temporelle :
-
timestamps en ms,
usleep/boucles contrôlées pour limiter la dérive tout en évitant la surconsommation CPU.
Contraintes respectées
- Langage : C
- Arguments :
number_of_philosophers,time_to_die,time_to_eat,time_to_sleep,[number_of_times_each_philosopher_must_eat] - Primitives de synchronisation (mutex / sémaphores selon version)
- Zéro leak + arrêt propre (threads join / cleanup des ressources)
- Logs formatés (timestamp + id) sans interleaving
Ce que ça m’a apporté
philosophers m’a appris à raisonner correctement sur la concurrence : identifier les sections critiques, choisir une stratégie anti-deadlock, protéger l’état partagé, et rendre une simulation stable même sous stress (timings serrés, beaucoup de philosophes). C’est un vrai déclic pour comprendre le scheduling et les effets de bord en multi-thread.
Bonus (Bientôt)
- Version processus + sémaphores (IPC) au lieu de threads/mutex.
- Stratégies alternatives (waiter global, optimisation de l’attente, logs améliorés).
Lancement
./philo number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]
Signification
number_of_philosophers: nombre de philosophes (et fourchettes)time_to_die: temps max sans manger avant de mourir (ms)time_to_eat: durée pour manger (ms)time_to_sleep: durée pour dormir (ms)- optionnel : nombre de repas requis pour arrêter la simulation (si tous ont atteint ce nombre)
Événements et sorties
Chaque action est loggée avec :
- un timestamp (ms depuis le début)
- l’id du philosophe
- l’action (prendre une fourchette / manger / dormir / penser / mort)
Conditions de fin
- Si un philosophe dépasse
time_to_diesans manger → mort → arrêt. - Si l’option “nombre de repas” est fournie et que tous l’atteignent → arrêt propre.
Stratégie (mandatory)
- Threads : 1 thread par philosophe
-
Mutex :
-
1 mutex par fourchette
- 1 mutex pour l’affichage/log (éviter l’interleaving)
- mutex pour l’état partagé (stop flag, last_meal, meals_count)
Éviter les deadlocks
Approches classiques :
- Ordre de prise différent selon l’id (pair/impair)
- Hiérarchie stricte (toujours prendre la plus petite fourchette id en premier)
- “Waiter” global (limiter le nombre de philosophes pouvant tenter de manger) (souvent bonus/alternative)
Monitoring
-
Un composant de surveillance (thread dédié ou boucle contrôlée) vérifie régulièrement :
-
now - last_meal > time_to_die→ déclenche la mort - À la fin : signaler l’arrêt et join tous les threads.
Précision et CPU
- Utiliser une fonction
get_time_ms()stable (ex.gettimeofday) -
Utiliser un
smart_sleep()(petitsusleep) pour éviter : -
trop de CPU
- dérives temporelles importantes