-
Notifications
You must be signed in to change notification settings - Fork 0
/
condition_variable4.cpp
172 lines (147 loc) · 5.41 KB
/
condition_variable4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
Problem:
Boss Thread
Starts threads
The threads have to notify the Boss the preparation for work has been done.
The Boss on getting all the notification with notify all the threads to start work
The threads will complete work and notify back and exit
Once all threads complete work Boss goes home
*/
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <atomic>
#include <cstdlib>
#include <ctime>
using namespace std;
std::atomic<int> preparedCount;
std::atomic<int> doneCount;
condition_variable workerToBossCondVariable;
condition_variable bossToWorkerCondVariable;
bool startWork = false;
bool goHome = false;
mutex startWorkMutex, goHomeMutex, coutMutex;
int getRandomTime(int start, int end);
class Worker {
// storing reference
string name;
public:
Worker(string n): name(n) {};
void operator() () {
int preparedTime = getRandomTime(5000, 7500);
// Sleep for a while to simulate work preparation
this_thread::sleep_for(std::chrono::milliseconds(preparedTime));
// Increment the preparedCount once work is done. This is being tracked by the BOSS
preparedCount++;
// Show preparation notice
coutMutex.lock();
cout << name << ": " << "Work prepared after " << preparedTime << " milliseconds. " << endl;
coutMutex.unlock();
// Notify BOSS that work is prepared
workerToBossCondVariable.notify_one();
// The below lock will wait for the BOSS notification to start work,
// and on notifiction only continue if startWork = true
{
// wait for the boss
unique_lock<mutex> startWorkLock(startWorkMutex);
bossToWorkerCondVariable.wait(startWorkLock, [] {
return startWork;
});
}
// Continue to finish the work
int workTime = getRandomTime(2000, 4000);
this_thread::sleep_for(std::chrono::milliseconds(workTime));
// Increment done count, also being tracked by the BOSS
doneCount++;
// Show work done notice
coutMutex.lock();
cout << name << ": " << "Work done after " << workTime << " milliseconds. " << endl;
coutMutex.unlock();
// Notify boss of completion
workerToBossCondVariable.notify_one();
// Wait for Boss's mesage to goHome
{
// wait for the boss
unique_lock<mutex> goHomeLock(goHomeMutex);
bossToWorkerCondVariable.wait(goHomeLock, [] {
return goHome;
});
}
}
};
int getRandomTime(int start, int end) {
srand(time(0));
return start + (rand() % (start + end));
}
int main() {
mutex preparedMutex, doneMutex;
//Prepared count is an atomic variable
//Only once instance across all threads
preparedCount.store(0);
cout << "Let's start working!!" << endl;
//Start the threads
thread a = thread(Worker("A"));
thread b = thread(Worker("B"));
thread c = thread(Worker("C"));
thread d = thread(Worker("D"));
thread e = thread(Worker("E"));
thread f = thread(Worker("F"));
//Start the work message from BOSS
coutMutex.lock();
cout << "BOSS: PREPARE YOUR WORK!!" << endl;
coutMutex.unlock();
// The Boss waits using a lock
unique_lock<mutex> prepareUniqueLock(preparedMutex);
// prepareUniqueLock will be locked. workerToBossCondVariable gets notified
// one by one by the workers but it only unlocks prepareUniqueLock when preparedCount becomes 6
workerToBossCondVariable.wait(prepareUniqueLock, [] {
return preparedCount == 6;
});
// Move on to working phase: Setting startWork to will unlock all the threads
// waiting for the start work notifications. If we don't set this then the threads
// will keep on waiting even if we notify since it is a predicate check inside
// the workers.
startWork = true;
coutMutex.lock();
cout << "BOSS: START YOUR WORK!!" << endl;
coutMutex.unlock();
// Initialize the done count atomic variable
doneCount.store(0);
// Notify all workers to start work
bossToWorkerCondVariable.notify_all();
// Same are working phase lock, wait for all to complete work
unique_lock<mutex> doneUniqueLock(doneMutex);
workerToBossCondVariable.wait(doneUniqueLock, [] {
return doneCount == 6;
});
// Unblock threads for completion
goHome = true;
coutMutex.lock();
cout << "BOSS: GO HOME!!" << endl;
coutMutex.unlock();
// Notify all workers to go home
bossToWorkerCondVariable.notify_all();
// Always join at the end
a.join(), b.join(), c.join(), d.join(), e.join(), f.join();
return 0;
}
/*
Output :
Let's start working!!
BOSS: PREPARE YOUR WORK!!
E: Work prepared after 5709 milliseconds.
D: Work prepared after 6599 milliseconds.
B: Work prepared after 6599 milliseconds.
C: Work prepared after 6599 milliseconds.
F: Work prepared after 6599 milliseconds.
A: Work prepared after 10135 milliseconds.
BOSS: START YOUR WORK!!
C: Work done after 6169 milliseconds.
B: Work done after 6169 milliseconds.
A: Work done after 6169 milliseconds.
F: Work done after 6169 milliseconds.
D: Work done after 6169 milliseconds.
E: Work done after 6169 milliseconds.
BOSS: GO HOME!!
*/