I'm right now working on a school projection and i have some problems with sync'ing 3 threads (2 threads + mian thread). The description says that i have to print 100x "ping" then 100x"pong" and 100x"\n", BUT in this order :
PingPong\n and so on...
when i start my code like I have it right now it just prints 100xPing then 100xPong and then 100x\n out, and I cant understand why :(
The Point which i cant understnad is , the while should stop when i set counter to 1 and it should open cond.wait(); after that it shoudl move to pong() and so on...
Here is the code:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>
using namespace std;
mutex m; // Mutex
// Bedingungsvariable
condition_variable cond;
atomic<int> counter = 0;
bool done= true;
void ping() {
unique_lock <mutex > lock{ m }; // m sperren
while (counter != 0) {
cond.wait(lock); //sperren und dann wieder freigeben
}
while (counter == 0) {
for (int i = 0; i < 100; i++) {
cout << "Ping";
counter = 1;
cond.notify_one();
}
}
}
void pong() {
unique_lock <mutex > lock{ m }; // m sperren
while (counter != 1) {
cond.wait(lock);
}
while (counter == 1) {
for (int i = 0; i < 100; i++) {
cout << "Pong";
counter = 2;
cond.notify_one();
}
}
}
int main() {
thread t1(pong);
thread t(ping); // Zweiten Thread starten
unique_lock <mutex > lock{ m }; // m sperren
while (counter != 2) cond.wait(lock);
while (counter == 2) {
for (int i = 0; i < 100; i++) {
cout << "\n";
counter = 0;
cond.notify_one();
}
}
lock.unlock(); // Mutex freigeben
t.join();
t1.join();
system("PAUSE");
return EXIT_SUCCESS;
}
I took the time to correct and provide feedback. Please take your time to learn and understand. I added comments in the code also.
#include <string>
, because no string
s are used in this program.bool done = true;
. I don't think you need it.#include <condition_variable>
if you want to use conditional variables (Bedingungsvariablen).atomic<int> counter = 0;
gives me an error (g++ 4.8.4, Ubuntu, C++11 flag on). Instead I replaced that line by atomic<int> counter{0};
.cond.wait(*lock*, *lambda*);
might look strange, but it does the same as the while(*condition*) cond.wait(*lock*);
of yours.notify_one()
notifies only one thread, but you have no control over which one will be awaken. Therefore (since you have more than two threads) I use notify_all()
.system("PAUSE")
. Not needed.Summary (of sync mechanism):
Basically each counter value (0, 1, 2) corresponds to a thread (you got that idea right I think).
However you skip the while
loops, because you want to loop/process only a hundred times; you just need to loop a hundred times.
So the synchronisation is best put into the respective for
loops and all follow the same pattern:
And so the threads can execute in turn.
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
using namespace std;
mutex m; // Mutex
condition_variable cond; // Bedingungsvariable
atomic<int> counter{0};
void ping() {
for (int i = 0; i < 100; i++) {
//cout << "Ping: m sperren und warten" << endl;
// m sperren und ...
unique_lock <mutex > lock{m};
//... dann wieder freigeben
//sobald counter == 0 gilt
cond.wait(lock, [] {
return counter == 0;
});
//Datenverarbeitung und ...
//... counter setzen für nächsten Thread
cout << "Ping";
counter = 1;
//cout << "Ping: m freigeben und benachrichtigen" << endl;
//m freigeben und
//andere Threads benachrichtigen
lock.unlock();
cond.notify_all();
}
}
void pong() {
for (int i = 0; i < 100; i++) {
//cout << "Pong: m sperren und warten" << endl;
//m sperren und ...
unique_lock <mutex > lock{m};
//... dann wieder freigeben
//sobald counter == 1 gilt
cond.wait(lock, [] {
return counter == 1;
});
//Datenverarbeitung und ...
//... counter setzen für nächsten Thread
cout << "Pong";
counter = 2;
//cout << "Pong: m freigeben und benachrichtigen" << endl;
//m freigeben und
//andere Threads benachrichtigen
lock.unlock();
cond.notify_all();
}
}
int main() {
thread t(ping); // ping Thread starten
thread t1(pong); // pong Thread starten
for (int i = 0; i < 100; i++) {
//cout << "\\n: m sperren und warten" << endl;
// m sperren und ...
unique_lock <mutex > lock{m};
//... dann wieder freigeben
//sobald counter == 2 gilt
cond.wait(lock, [] {
return counter == 2;
});
//Datenverarbeitung und ...
//... counter setzen für nächsten Thread
cout << endl;
counter = 0;
//cout << "\\n: m freigeben und benachrichtigen" << endl;
//m freigeben und
//andere Threads benachrichtigen
lock.unlock();
cond.notify_all();
}
t.join();
t1.join();
return EXIT_SUCCESS;
}
Note 1:
This does not work:
atomic<int> counter = 0;
This works:
atomic<int> counter(0);
or
atomic<int> counter{0};
or
atomic<int> counter;
...
counter = 0;