Spring 2021
Instructors Roz Cyrus and Jerry Cain
PDF
static void ticketAgent(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
handleCall(); // sleep for a small amount of time to emulate conversation time.
remainingTickets--;
cout << oslock << "Agent #" << id << " sold a ticket! (" << remainingTickets
<< " more to be sold)." << endl << osunlock;
if (shouldTakeBreak()) // flip a biased coin
takeBreak(); // if comes up heads, sleep for a random time to take a break
}
cout << oslock << "Agent #" << id << " notices all tickets are sold, and goes home!"
<< endl << osunlock;
}
int main(int argc, const char *argv[]) {
thread agents[10];
size_t remainingTickets = 250;
for (size_t i = 0; i < 10; i++)
agents[i] = thread(ticketAgent, 101 + i, ref(remainingTickets));
for (thread& agent: agents) agent.join();
cout << "End of Business Day!" << endl;
return 0;
}
poohbear@myth61:$ ./confused-ticket-agents
Agent #110 sold a ticket! (249 more to be sold).
Agent #104 sold a ticket! (248 more to be sold).
Agent #106 sold a ticket! (247 more to be sold).
// some 245 lines omitted for brevity
Agent #107 sold a ticket! (1 more to be sold).
Agent #103 sold a ticket! (0 more to be sold).
Agent #105 notices all tickets are sold, and goes home!
Agent #104 notices all tickets are sold, and goes home!
Agent #108 sold a ticket! (4294967295 more to be sold).
Agent #106 sold a ticket! (4294967294 more to be sold).
Agent #102 sold a ticket! (4294967293 more to be sold).
Agent #101 sold a ticket! (4294967292 more to be sold).
// carries on for a very, very, very long time
0x0000000000401a9b <+36>: mov -0x20(%rbp),%rax
0x0000000000401a9f <+40>: mov (%rax),%eax
0x0000000000401aa1 <+42>: lea -0x1(%rax),%edx
0x0000000000401aa4 <+45>: mov -0x20(%rbp),%rax
0x0000000000401aa8 <+49>: mov %edx,(%rax)
class mutex {
public:
mutex(); // constructs the mutex to be in an unlocked state
void lock(); // acquires the lock on the mutex, blocking until it's unlocked
void unlock(); // releases the lock and wakes up another threads trying to lock it
};
class mutex {
public:
mutex(); // constructs the mutex to be in an unlocked state
void lock(); // acquires the lock on the mutex, blocking until it's unlocked
void unlock(); // releases the lock and wakes up another threads trying to lock it
};
When there are multiple threads writing to a variable.
When there is a thread writing and one or more threads reading
Why do you not need a mutex when there are no writers (only readers)?
static void ticketAgent(size_t id, size_t& remainingTickets, mutex& ticketsLock) {
while (true) {
ticketsLock.lock();
if (remainingTickets == 0) break;
handleCall();
remainingTickets--;
cout << oslock << "Agent #" << id << " sold a ticket! (" << remainingTickets
<< " more to be sold)." << endl << osunlock;
ticketsLock.unlock();
if (shouldTakeBreak())
takeBreak();
}
ticketsLock.unlock();
cout << oslock << "Agent #" << id << " notices all tickets are sold, and goes home!"
<< endl << osunlock;
}
critical region
static void philosopher(size_t id, mutex& left, mutex& right) {
for (size_t i = 0; i < 3; i++) {
think(id);
eat(id, left, right);
}
}
int main(int argc, const char *argv[]) {
mutex forks[5];
thread philosophers[5];
for (size_t i = 0; i < 5; i++) {
mutex& left = forks[i], & right = forks[(i + 1) % 5];
philosophers[i] = thread(philosopher, i, ref(left), ref(right));
}
for (thread& p: philosophers) p.join();
return 0;
}
images courtesy of Roz Cyrus
static void think(size_t id) {
cout << oslock << id << " starts thinking." << endl << osunlock;
sleep_for(getThinkTime());
cout << oslock << id << " all done thinking. " << endl << osunlock;
}
static void eat(size_t id, mutex& left, mutex& right) {
left.lock();
right.lock();
cout << oslock << id << " starts eating om nom nom nom." << endl << osunlock;
sleep_for(getEatTime());
cout << oslock << id << " all done eating." << endl << osunlock;
left.unlock();
right.unlock();
}
static void eat(size_t id, mutex& left, mutex& right) {
left.lock();
sleep_for(5000); // artificially force off the processor
right.lock();
cout << oslock << id << " starts eating om nom nom nom." << endl << osunlock;
sleep_for(getEatTime());
cout << oslock << id << " all done eating." << endl << osunlock;
left.unlock();
right.unlock();
}