Because threads can become blocked
If you try running a program and it deadlocks right away, you immediately know you have a problem, and you can track it down. The real problem is when your program seems to be working fine but has the hidden potential to deadlock. In this case, you may get no indication that deadlocking is a possibility, so it will be latent in your program until it unexpectedly happens to a customer. (And you probably won’t be able to easily reproduce it.) Thus, preventing deadlock through careful program design is a critical part of developing concurrent programs.
Let’s look at the classic demonstration of deadlock, invented by Edsger Dijkstra: the
A difficulty is introduced into the problem: as philosophers, they have very little money, so they can only afford five chopsticks. These are spaced around the table between them. When a philosopher wants to eat, they must pick up the chopstick to the left and the one to the right. If the philosopher on either side is using a desired chopstick, our philosopher must wait until the necessary chopsticks become available.
//: C11:DiningPhilosophers.h
// Classes for Dining Philosohophers
#ifndef DININGPHILOSOPHERS_H
#define DININGPHILOSOPHERS_H
#include "zthread/Condition.h"
#include "zthread/Guard.h"
#include "zthread/Mutex.h"
#include "zthread/Thread.h"
#include "Display.h"
#include
#include
class Chopstick {
ZThread::Mutex lock;
ZThread::Condition notTaken;
bool taken;
public:
Chopstick() : notTaken(lock), taken(false) {}
void take() {
ZThread::Guard
while(taken)
notTaken.wait();
taken = true;
}
void drop() {
ZThread::Guard
taken = false;
notTaken.signal();
}
};
class Philosopher : public ZThread::Runnable {
Chopstick& left;
Chopstick& right;
int id;
int ponderFactor;
ZThread::CountedPtr
int randSleepTime() {
if(ponderFactor == 0) return 0;
return rand()/(RAND_MAX/ponderFactor) * 250;
}
public:
Philosopher(Chopstick& l, Chopstick& r,
ZThread::CountedPtr
: left(l), right(r), display(disp),
id(ident), ponderFactor(ponder) { srand(time(0)); }
virtual void run() {
try {
while(!ZThread::Thread::interrupted()) {
{
std::ostringstream os;
os << *this << " thinking" << std::endl;
display->output(os);
}
ZThread::Thread::sleep(randSleepTime());
// Hungry
{
std::ostringstream os;
os << *this << " grabbing right" << std::endl;
display->output(os);
}
right.take();
{
std::ostringstream os;
os << *this << " grabbing left" << std::endl;
display->output(os);
}
left.take();
// Eating
{
std::ostringstream os;
os << *this << " eating" << std::endl;
display->output(os);
}
ZThread::Thread::sleep(randSleepTime());
right.drop();
left.drop();
}
} catch(ZThread::Synchronization_Exception& e) {
std::ostringstream os;
os << *this << " " << e.what() << std::endl;
display->output(os);
}
}
friend std::ostream&
operator<<(std::ostream& os, const Philosopher& p) {
return os << "Philosopher " << p.id;
}
};
#endif // DININGPHILOSOPHERS_H ///:~
No two Philosophers can take( ) a Chopstick at the same time, since take( ) is synchronized with a Mutex. In addition, if the chopstick has already been taken by one Philosopher, another can wait( ) on the available Condition until the Chopstick becomes available when the current holder calls drop( ) (which must also be synchronized to prevent race conditions).