Lock Challenge Step 4
Syncmultiplethreadssharingaresource
main_no_debug.cpp
Go to the documentation of this file.
1 
14 
15 
16 #include <iostream> // std::cout
17 #include <thread> // std::thread
18 #include <mutex> // std::mutex, std::adopt_lock, std::unique_lock
19 #include <chrono> // std::chrono
20 #include <condition_variable> // std::condition_variable
21 #include <random> // std::default_random_generator
22 
23 #include "LockGuard.h" // chal::LockGuard
24 
25 #define NUM_THDS 3 // number of threads to execute
26 
27 
28 // Globals
29 std::mutex mtx; // shared mutex to signal threads
30 std::mutex prnt_mtx; // shared mutex to print from threads
31 std::condition_variable cond; // shared condition variable
32 
33 
34 
44 void thd_printer(int id, std::string msg) {
45 
46  prnt_mtx.lock();
47 
48  chal::LockGuard<std::mutex> lck (prnt_mtx, std::adopt_lock);
49 
50  std::cout << "thread" << id+1 << ": " << msg << std::endl;
51 }
52 
53 
65 void thd_worker (const int id, int &next_thd, std::default_random_engine &rand_e) {
66 
67  int wait_tm; // time to randomize
68 
69  thd_printer(id, "starting, waiting.");
70 
71  // repeat forever
72  while(1) {
73 
74  // lock mutex
75  mtx.lock();
76  std::unique_lock<std::mutex> locker (mtx, std::adopt_lock);
77 
78  // wait for condition signal
79  // Upon condition signal, check if current thread is next
80  // if yes continue, if not keep waiting
81  // lambda function creates condition predicate
82  cond.wait(locker, [&]() { return id == next_thd; });
83 
84  thd_printer(id, "signal received, doing work ....");
85 
86  // generate pseudo-random value between 1 and 5 seconds
87  wait_tm = 1 + rand_e() % 5;
88 
89  // sleep to simulate work
90  std::this_thread::sleep_for(std::chrono::seconds(wait_tm));
91 
92  thd_printer(id, "done with work, signal next thread");
93 
94  // if topmost thread, reset next_thd
95  if( next_thd == NUM_THDS-1 ) {
96  next_thd = 0;
97  }
98  else {
99  ++next_thd;
100  }
101 
102  cond.notify_all();
103  }
104 }
105 
106 
107 
108 int main () {
109  //bool DEBUG = true;
110  int id = -1; // identifier to pass to Debug Printer
111 
112  std::thread threads[NUM_THDS]; // create an array of NUM_THDS thread objects
113  int next_thd = -1; // prevent a spurious wake from prematurely activating thread 0
114 
115  std::default_random_engine rand_e; // create a pseudo-random engine
116 
117  std::cout << "main: starting all threads" << std::endl;
118 
119  // spawn NUM_THDS threads:
120  for (int i=0; i<NUM_THDS; ++i) {
121 
122  // populate the array of thread objects
123  // pass in: * their unique ID by value
124  // * an integer to keep track of thread order by reference
125  // * a shared psuedo-random number generator by reference
126  threads[i] = std::thread(thd_worker, i, std::ref(next_thd), std::ref(rand_e));
127  }
128 
129  // wait for 3 seconds
130  std::this_thread::sleep_for(std::chrono::seconds(3));
131 
132  cond.notify_all(); // start sequence
133  next_thd = 0; // allow thread 0 to be activated
134 
135  // clean up
136  for ( auto& th : threads ) {
137  th.join();
138  }
139 
140  return 0;
141 }
#define NUM_THDS
void thd_printer(int id, std::string msg)
thread print function
A movable scoped lock type.
Definition: LockGuard.h:34
std::mutex mtx
std::condition_variable cond
std::mutex prnt_mtx
void thd_worker(const int id, int &next_thd, std::default_random_engine &rand_e)
thread worker function
int main()
contains namespace chal and class LockGuard