Classical IPC Problems - The Dining Philosophers Problem
Dining Philosopher Problem Using Semaphores
Refer to Dining Philosopher Problem Using Semaphores.
如下图所示,哲学家需要左右两把叉子才能进食。五把叉子可以允许两位哲学家进行就餐。
Semaphore Solution to Dining Philosopher –
process P[i]
while true do
{ THINK;
PICKUP(CHOPSTICK[i], CHOPSTICK[i+1 mod N]);
EAT;
PUTDOWN(CHOPSTICK[i], CHOPSTICK[i+1 mod N])
}
where N is the number of Philosophers.
There are three states of philosopher : THINKING, HUNGRY and EATING. Here there are two semaphores : Mutex and a semaphore array for the philosophers. Mutex is used such that no two philosophers may access the pickup or putdown at the same time. The array is used to control the behavior of each philosopher. But, semaphores can result in deadlock due to programming errors.
/* Dining Philosopher Problem Using Semaphores */
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#define N 5
#define THINKING 2
#define HUNGRY 1
#define EATING 0
#define LEFT (phnum + N - 1) % N
#define RIGHT (phnum + 1) % N
int state[N]; // 数组state跟踪每一个哲学家是在进餐、思考还是饥饿状态
int phil[N] = { 0, 1, 2, 3, 4 };
sem_t mutex; // Mutex is used such that no two philosophers may access the pickup or putdown at the same time.
sem_t S[N]; // The array is used to control the behavior of each philosopher. But, semaphores can result in deadlock due to programming errors.
void test(int phnum)
{
if (state[phnum] == HUNGRY
&& state[LEFT] != EATING
&& state[RIGHT] != EATING) { // 当哲学家phnum的左右两位邻居哲学家不在进餐状态,则可以获取左右两边的叉子
// state that eating
state[phnum] = EATING;
sleep(2); // 这里的sleep是为了什么?
printf("Philosopher %d takes fork %d and %d\n",
phnum + 1, LEFT + 1, phnum + 1); // 为什么不直接输出phnum 而要输出LEFT+1?
printf("Philosopher %d is Eating\n", phnum + 1);
// sem_post(&S[phnum]) has no effect
// during takefork
// used to wake up hungry philosophers
// during putfork ???
sem_post(&S[phnum]);
}
}
// take up chopsticks
void take_fork(int phnum)
{
sem_wait(&mutex); // 进入临界区
// state that hungry
state[phnum] = HUNGRY; // 记录哲学家phnum处于饥饿状态
printf("Philosopher %d is Hungry\n", phnum + 1);
// eat if neighbours are not eating
test(phnum); // 尝试获取两把叉子
sem_post(&mutex); // 离开临界区
// if unable to eat wait to be signalled
sem_wait(&S[phnum]); // 如果得不到需要的叉子则阻塞
// 等到了之后就sleep吗?
// 一个哲学界对应一个thread
// 没等到邻居put_forks的post,则阻塞在这里,不作下一轮take_forks的test?
sleep(1);
}
// put down chopsticks
void put_fork(int phnum)
{
sem_wait(&mutex);
// state that thinking
state[phnum] = THINKING;
printf("Philosopher %d putting fork %d and %d down\n",
phnum + 1, LEFT + 1, phnum + 1);
printf("Philosopher %d is thinking\n", phnum + 1);
test(LEFT);
test(RIGHT);
sem_post(&mutex);
}
void* philospher(void* num)
{
while (1) {
int* i = num;
/*
#include <unistd.h>
unsigned int sleep(unsigned int seconds); // sleep - sleep for a specified number of seconds
*/
sleep(1);
take_fork(*i);
sleep(0); // Sleep(0) 的意义是放弃当前线程执行的时间片,把自身放到等待队列之中。这时其它的线程就会得到时间片进行程序的程序。
put_fork(*i);
}
}
int main()
{
int i;
pthread_t thread_id[N];
/*
int sem_init(sem_t *sem, int pshared, unsigned int value);
The pshared argument indicates whether this semaphore is to be shared
between the threads of a process (value 0), or between processes (nonzero).
If pshared has the value 0, then the semaphore is shared between the
threads of a process, and should be located at some address that is
visible to all threads (e.g., a global variable, or a variable
allocated dynamically on the heap).
*/
// initialize the semaphores
sem_init(&mutex, 0, 1);
for (i = 0; i < N; i++)
sem_init(&S[i], 0, 0);
for (i = 0; i < N; i++) {
/*
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
*/
// create philosopher processes
pthread_create(&thread_id[i], NULL,
philospher, &phil[i]);
printf("Philosopher %d is thinking\n", i + 1);
}
for (i = 0; i < N; i++)
/*
int pthread_join(pthread_t thread, void **retval);
*/
pthread_join(thread_id[i], NULL);
}
输出
Philosopher 1 is thinking
Philosopher 2 is thinking
Philosopher 3 is thinking
Philosopher 4 is thinking
Philosopher 5 is thinking
Philosopher 1 is Hungry
Philosopher 2 is Hungry
Philosopher 5 is Hungry
Philosopher 3 is Hungry
Philosopher 4 is Hungry
Philosopher 4 takes fork 3 and 4
Philosopher 4 is Eating
Philosopher 4 putting fork 3 and 4 down
Philosopher 4 is thinking
Philosopher 3 takes fork 2 and 3
Philosopher 3 is Eating
Philosopher 5 takes fork 4 and 5
Philosopher 5 is Eating
Philosopher 3 putting fork 2 and 3 down
Philosopher 3 is thinking
Philosopher 2 takes fork 1 and 2
Philosopher 2 is Eating
Philosopher 4 is Hungry
Philosopher 5 putting fork 4 and 5 down
Philosopher 5 is thinking
Philosopher 4 takes fork 3 and 4
Philosopher 4 is Eating
Philosopher 3 is Hungry
Philosopher 2 putting fork 1 and 2 down
Philosopher 2 is thinking
Philosopher 1 takes fork 5 and 1
Philosopher 1 is Eating
Philosopher 5 is Hungry
Philosopher 4 putting fork 3 and 4 down
Philosopher 4 is thinking
......
Dining-Philosophers Solution Using Monitors
Refer to Dining-Philosophers Solution Using Monitors
Monitors in Process Synchronization
- It is the collection of condition variables and procedures combined together in a special kind of module or a package.
- The processes running outside the monitor can’t access the internal variable of the monitor but can call procedures of the monitor.
- Only one process at a time can execute code inside monitors.
/* Dining-Philosophers Solution Using Monitors */
#define N 5
#define THINKING 2
#define HUNGRY 1
#define EATING 0
#define LEFT (i + N - 1) % N
#define RIGHT (i + 1) % N
// Dining-Philosophers Solution Using Monitors
monitor DP
{
status state[N]; // 数组state跟踪每一个哲学家是在进餐、思考还是饥饿状态
condition self[N];
// Pickup chopsticks
Pickup(int i)
{
// indicate that I’m HUNGRY
state[i] = HUNGRY;
// set state to EATING in test()
// only if my left and right neighbors
// are not EATING
test(i);
// if unable to eat, wait to be signaled
if (state[i] != EATING)
self[i].wait;
}
// Put down chopsticks
Putdown(int i)
{
// indicate that I’m THINKING
state[i] = THINKING;
// if right neighbor R=(i + 1) % N is HUNGRY and
// both of R’s neighbors are not EATING,
// set R’s state to EATING and wake it up by
// signaling R’s CV
test(LEFT);
test(RIGHT);
}
test(int i)
{
if (state[i] == HUNGRY
&& state[LEFT] != EATING
&& state[RIGHT] != EATING) {
// indicate that I’m EATING
state[i] = EATING;
// signal() has no effect during Pickup(),
// but is important to wake up waiting
// HUNGRY philosophers during Putdown()
self[i].signal();
}
}
init()
{
// Execution of Pickup(), Putdown() and test()
// are all mutually exclusive,
// i.e. only one at a time can be executing
// 因为写在monitor里面,每次确保只有一个线程可以使用monitor,从而不需要mutex?
for
i = 0 to N
// Verify that this monitor-based solution is
// deadlock free and mutually exclusive in that
// no 2 neighbors can eat simultaneously
state[i] = THINKING;
}
} // end of monitor