This is how I solve it using current c++ standard (practically using c++11).
There is no need to check in which thread we are, nor to check if the number is currently odd or even. The control is passed between the threads after each increment.
Note that there will be no deadlock:
A notify happens before wait, and they both are under the lock. Hence when a waiting thread gives the other thread a chance to lock, the other thread will reach notify before reaching its own wait.
Note also that a thread being notified might block if the notifying thread has not yet started waiting. But this block is only momentarily and has no influence on the threads taking each its own turn to do the printing and increment.
#include <iostream>
#include <thread>
#include <mutex>
#include <future>
using namespace std;
mutex mtx;
condition_variable cv;
int counter = 1;
void printNumbers(int threadId) {
while (true) {
unique_lock<mutex> lock(mtx);
cout << "Thread " << threadId << ": " << counter << endl;
counter++;
cv.notify_all();
cv.wait(lock);
}
}
int main() {
thread thread1(printNumbers, 1);
thread thread2(printNumbers, 2);
thread1.join();
thread2.join();
return 0;
}
The above code is a solution. However, it is undetermined which of the threads will be counting the odd numbers and which will be counting the even numbers.
It will be incorrect to try to solve it by switching the order of wait-notify for the thread of the even numbers. Doing that will not guarantee anymore that there will be no deadlock:
When a waiting thread gives the other a chance to lock, the other will reach its own wait without first notifying.
If we want thread 1 to be the one printing the odd numbers, a less elegant solution would be to add a simple condition as follows:
void printNumbers(int threadId) {
while (true) {
if (counter == 1 && threadId != 1 )
continue;
unique_lock<mutex> lock(mtx);
cout << "Thread " << threadId << ": " << counter << endl;
counter++;
cv.notify_all();
cv.wait(lock);
}
}
I call it less elegant because this means that as long as the other thread did not start, the loop is busy-waiting.
So we will simply wait if we are in thread 2 and counter is still 1.
Note that the only situation of actually waiting there is when counter was 1 when locking, thus it is guaranteed that thread 1 is not waiting, and will notify.
void printNumbers(int threadId) {
if (threadId == 2) {
unique_lock<mutex> lock(mtx);
if (counter == 1)
cv.wait(lock);
}
while (true) {
unique_lock<mutex> lock(mtx);
cout << "Thread " << threadId << ": " << counter << endl;
counter++;
cv.notify_all();
cv.wait(lock);
}
}