1
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;
int num = 1;
#define NUM 20
condition_variable odd;
condition_variable even;
mutex mut;
void thread_odd()
{
        while(num < NUM -1)
        {
            if(num%2 != 1)
            {
                  unique_lock<mutex> lock(mut);
                  odd.wait(lock);
            }
            cout<<"ODD : "<<num<<endl;
            num++;
            even.notify_all();  // Line X
        }
}
void thread_even()
{
        while(num < NUM )
        {
            if(num%2 != 0)
            {
                  unique_lock<mutex> lock(mut);
                  even.wait(lock);
            }
            cout<<"EVEN : "<<num<<endl;
            num++;
            odd.notify_all();
        }
}
int main()
{
       thread t1(thread_odd), t2(thread_even);
       t1.join();
       t2.join();
       return 0;
}

/* Above is the program to print ODD & EVEN numbers in synchronized manner ( one by one ) . The code is working fine most of the time . But it is getting into a deadlock situation sometimes . That is happening when odd thread is hitting notify_all but before the even thread wakes up it odd thread acquires the lock and then as it finds wait condition it goes into wait while the even thread hasn't wake up . Leaving a dealock situation . I tried replacing notify_all to notify_one , but the problem still persists . Is there any change in the design required ? Or is there anything which I am missing completely ? */

user3798283
  • 467
  • 3
  • 10
  • You need to make sure that `thread_even()` calls `even.wait()` before `thread_odd()` calls `even.notify_all()`. – mkcms Mar 22 '17 at 20:20

1 Answers1

2

As a general rule in a concurrent program, when you want to access a shared resource to read it and modify it (in your case, modulo operator on num is first reading and num++ is writing), you need to obtain mutual exclusive access to that resource and not release it until you're done with that resource.

Your lock is going to be released when it exists the if-statement scope so you are not following this rule.

If you modify your code as follows, you won't deadlock:

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;
int num = 1;
#define NUM 20
condition_variable odd;
condition_variable even;
mutex mut;
void thread_odd()
{
    while(num < NUM -1)
    {
        unique_lock<mutex> lock(mut);
        if(num%2 != 1)
        {
            odd.wait(lock);
        }
        cout<<"ODD : "<<num<<endl;
        num++;
        lock.unlock();
        even.notify_all();  // Line X
    }
}
void thread_even()
{
    while(num < NUM )
    {
        unique_lock<mutex> lock(mut);
        if(num%2 != 0)
        {
            even.wait(lock);
        }
        cout<<"EVEN : "<<num<<endl;
        num++;
        lock.unlock();
        odd.notify_all();
    }
}
int main()
{
    thread t1(thread_odd), t2(thread_even);
    t1.join();
    t2.join();
    return 0;
}

Notice how I am releasing the lock before notifying. In C++ this is not only possible (as opposed to Java) but recommended as you will decrease the chances of having the releaser greedily re-enter the critical block. You'll get some more insights into this last point here.

Community
  • 1
  • 1
Edd
  • 1,350
  • 11
  • 14