|
Philosophers: The philosopher struct (or class)
contains information regarding the current state of each philosophers. The
three states are thinking, hungry, and eating. A
user-defined enumeration may be used.
enum
Action {
THINKING,
HUNGRY,
EATING,
};
Resource Sharing:
Furthermore, each philosopher will have a binary semaphore. In the case
where the philosopher is hungry but unsuccessful in acquiring the forks, the
semaphore will block until the forks are available. The CSemaphore class may
be used for implementing the semaphores.
To ensure that no deadlock will occurs, each philosopher will make sure
that both forks are available before taking either one of them. By
using a mutex, only one philosopher may try to acquire the forks and start
eating at one time. The CMutex class may be used for implementing the mutex.
Thinking & Eating:
The main process will be alternating the philosophers between eating and
thinking. More specifically, the sequence should be thinking, taking forks,
eating, putting down forks, and repeat. For this simulation, the Win32
function sleep() will be used for the sleeping and eating processes. Each
philosopher will eat and think for a random amount of time.
Take Forks & Put Forks:
Basically, before taking the forks, a philosopher will enter a critical
region and see if both forks are available for taking. If both forks are
free, the philosopher will enter the EATING state and exit the critical
region. If the forks are unavailable at the moment, the philosopher will
remain HUNGRY, exit the critical region, and block. Upon replacing the fork,
the philosopher will return to THINKING while freeing the forks for the
neighbours within a critical region.
int number = 5;
void
PutForks(int current) {
mutex.Lock();
philosopher[current].state = THINKING;
SendMessage(STATE_CHANGE);
Test((current+number-1)%number);
Test((current+1)%number);
mutex.Unlock();
}
void
TakeForks(int current) {
mutex.Lock();
philosopher[current].state = HUNGRY;
SendMessage(STATE_CHANGE);
Test(current);
mutex.Unlock();
philosopher[current].semaphore->Lock();
}
void
Test(int current) {
if ((philosopher[current].state == HUNGRY)&&
(philosopher[(current+number-1)%number].state != EATING)&&
(philosopher[(current+1)%number].state != EATING)) {
philosopher[current].state = EATING;
SendMessage(STATE_CHANGE);
philosopher[current].semaphore->Unlock();
}
}
Threads:
Each philosopher is controlled by a worker thread. The AfxBeginThread()
function may be used for starting the threads, one function call per thread.
AfxBeginThread(ThreadProc, reinterpret_cast<LPVOID>(current));
The thread functions must be a static function with a
return type UINT:
static
UINT ThreadProc(LPVOID p);
UINT
ThreadProc(LPVOID p) {
int current=reinterpret_cast<int>(p);
while (is_simulating) {
Think();
TakeForks(current);
Eat();
PutForks(current);
}
return 0;
}
For communications between the UI thread and the worker
threads (for the changing of a philosopher's state), the PostMessage() or
SendMessage() function may be used. Upon receiving the messages from the
worker threads, the UI thread will update the graphics on the output screen
accordingly.
The thread will end with the ThreadProc() function.
Drawing the Plates and Forks (Chopsticks):
The coordinates for the plates and forks may be
calculated with simple trigonometric functions from <math.h>. The necessary
data are the center point of the window, the radius (distance from the
center point to the plate), and the degree of the plate with respect to
north. For example, if there are 5 plates, the degree for each plate will be
0, 0.4 p, 0.8 p,
1.2 p, and
1.6 p. For translating from polar coordinates to
Cartesian coordinates, the sin() and cos() math function may be used.

Similar techniques may be applied for
calculating the coordinates of the forks. |