Multiple threads are often spawned so they can subdivide and collectively solve a larger problem.
- Consider the case where 10 ticket agents answer telephones at United Airlines to jointly sell 1000 airline tickets (looking for full program? click here):
static const unsigned int kBaseIDNumber = 101;
static const unsigned int kNumAgents = 10;
static const unsigned int kNumTickets = 1000;
static mutex ticketsLock;
static unsigned int remainingTickets = kNumTickets;
static void ticketAgent(size_t id) {
while (true) {
ticketsLock.lock();
if (remainingTickets == 0) break;
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;
}
int main(int argc, const char *argv[]) {
thread agents[kNumAgents];
for (size_t i = 0; i < kNumAgents; i++)
agents[i] = thread(ticketAgent, kBaseIDNumber + i);
for (thread& agent: agents)
agent.join();
cout << "End of Business Day!" << endl;
}
- Yes, we use global variables, but our defense here is that multiple threads all need access to a shared resource. static global variables are not at all uncommon in multithreaded programs (although larger programs would probably package them in a dedicated library or module).
- There's a significant critical region in this program, and we use a mutex object to mark the beginning and end of it.
- We'll first write this program without the mutex to illustrate what the problems are.
- We'll then insert the mutex to understand exactly how it solves it.