Hey there! π Welcome to my Philosophers project, where I built my solution for the classic dining philosophers problem - a challenge in concurrent programming and thread synchronization!
Imagine a group of philosophers sitting at a round table, doing one of three things:
- π€ Thinking
- π΄ Eating
- π΄ Sleeping
But here's the catch:
- There's a large bowl of spaghetti in the middle
- There's a fork between each philosopher
- They need TWO forks to eat
- If they don't eat for too long, they die
- They can't talk to each other
- They can't know if another philosopher is about to die
The challenge? Keep them all alive by properly managing their actions and resource sharing!
I implemented this project using threads and mutexes in C, with a focus on preventing deadlocks and race conditions:
-
Thread Management:
- Each philosopher is a thread
- Used pthread library for thread creation and management
- Implemented mutex locks for forks and shared resources
- Added colorful output for better visualization of states
-
Data Structures:
- Created a main data structure (
t_data) for shared information:- Number of philosophers
- Time limits for eating, sleeping, and dying
- Mutex arrays for forks
- Status flags for simulation control
- Individual philosopher structure (
t_philo) containing:- Personal data (ID, meals eaten, last meal time)
- Pointers to left and right forks
- Thread and mutex information
- Created a main data structure (
-
Synchronization Strategy:
- Implemented even/odd philosopher timing to prevent deadlocks
- Used mutex locks for critical sections
- Added monitoring system to check philosopher states
- Implemented clean termination when conditions are met
The Dining Philosophers project presented three fundamental challenges in concurrent programming.
The most critical challenge was protecting shared resources from simultaneous access. Each philosopher needed to track their meal times and status without interference from others. I implemented precise mutex locks around critical sections and developed a careful timing system for death detection that maintained data consistency while allowing maximum concurrency.
Deadlock prevention was crucial for system stability. The classic deadlock scenario occurs when each philosopher grabs their left fork and waits indefinitely for their right fork. I solved this through a strategic fork allocation system where philosophers pick up forks in a consistent order. This, combined with proper resource release timing, ensures the system never reaches a deadlock state.
Balancing safety with efficiency required careful tuning. I minimized mutex lock durations to reduce thread contention while maintaining data integrity. The monitoring system was optimized to check philosopher states efficiently without overwhelming the CPU. These optimizations resulted in smooth thread operations without sacrificing safety.
This project provided deep insights into concurrent programming and system design.
Implementing the dining philosophers simulation revealed the complexities of thread synchronization. I learned to identify and prevent race conditions through careful mutex usage. The project demonstrated how theoretical concurrency concepts apply to real-world problems, especially in preventing deadlocks while maintaining system performance.
Working with POSIX threads enhanced our understanding of low-level system operations. I gained practical experience in resource management and thread scheduling. The project taught me how to balance system resources effectively while maintaining precise timing requirements for philosopher actions.
Debugging multi-threaded applications required developing systematic approaches to complex problems. I created efficient monitoring systems to track philosopher states and detect potential issues early. These debugging and optimization skills proved essential for developing robust concurrent applications.
# Compile the program
make
# Run with parameters:
./philo number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]
# Example:
./philo 5 800 200 200 7number_of_philosophers: Number of philosophers and forkstime_to_die: Time (ms) a philosopher can survive without eatingtime_to_eat: Time (ms) it takes to eattime_to_sleep: Time (ms) spent sleeping[number_of_times_each_philosopher_must_eat]: Optional, simulation stops when all philosophers eat this many times
I added color coding to make it easier to follow what's happening:
- π΅ BLUE: Thinking
- π’ GREEN: Eating
- π‘ YELLOW: Taking forks
- π£ CYAN: Sleeping
- π΄ RED: Death (hopefully you won't see this much!)
The program handles various error cases:
- Invalid arguments
- Memory allocation failures
- Thread creation failures
- Mutex initialization errors
- Edge cases (single philosopher, zero meals, etc.)
This project was a fantastic dive into concurrent programming! It really showed me how complex managing shared resources can be, and how important proper synchronization is in multi-threaded applications.
The skills I learned here - especially about thread management and race condition prevention - have been invaluable for understanding real-world concurrent programming challenges. Plus, watching the philosophers successfully share their forks without dying is pretty satisfying! π
Built with β€οΈ and lots of coffee at 42 School