Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please explain the use of condition variables in c++ threads, and why do we need to use `unique_lock` and `mutex` alongwith this

I am refering to this particular piece of code:

this code basically has three threads 1. Perform some handshaking with server 2. Load Data from XML files. 3. Do processing on data loaded from XML. As we can see that Task 1 is not dependent on any other Tasks but Task 3 is dependent on Task 2. So, it means Task 1 and Task 2 can be run in parallel by different Threads to improve the performance of application. Thus, application is built to be multithreaded.

#include <iostream>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
using namespace std::placeholders;

class Application
{
  std::mutex m_mutex;
  std::condition_variable m_condVar;
  bool m_bDataLoaded;
public:
  Application()
  {
    m_bDataLoaded = false;
  }
  void loadData()
  {
   // Make This Thread sleep for 1 Second
   std::this_thread::sleep_for(std::chrono::milliseconds(1000));
   std::cout<<"Loading Data from XML"<<std::endl;
   // Lock The Data structure
   std::lock_guard<std::mutex> guard(m_mutex);
   // Set the flag to true, means data is loaded
   m_bDataLoaded = true;
   // Notify the condition variable
   m_condVar.notify_one();
  }
  bool isDataLoaded()
  {
    return m_bDataLoaded;
  }
  void mainTask()
  {
    std::cout<<"Do Some Handshaking"<<std::endl;
    // Acquire the lock
    std::unique_lock<std::mutex> mlock(m_mutex);
    // Start waiting for the Condition Variable to get signaled
    // Wait() will internally release the lock and make the thread to block
    // As soon as condition variable get signaled, resume the thread and
    // again acquire the lock. Then check if condition is met or not
    // If condition is met then continue else again go in wait.
    m_condVar.wait(mlock, std::bind(&Application::isDataLoaded, this));
    std::cout<<"Do Processing On loaded Data"<<std::endl;
  }
};
int main()
{
   Application app;
   std::thread thread_1(&Application::mainTask, &app);
   std::thread thread_2(&Application::loadData, &app);
   thread_2.join();
   thread_1.join();
   return 0;
}

this code is from http://thispointer.com/c11-multithreading-part-7-condition-variables-explained/

Thanks

like image 634
Nilesh Kumar Avatar asked May 14 '18 13:05

Nilesh Kumar


People also ask

Why does a condition variable need a mutex?

Viewed in isolation, a condition variable allows threads to block and to be woken by other threads. However, condition variables are designed to be used in a specific way; a condition variable interacts with a mutex to make it easy to wait for an arbitrary condition on state protected by the mutex.

Why do I need condition variables why not use mutex locks for all synchronization problems?

While mutex implement synchronization by controlling thread access to data, condition variables allow threads to synchronize based upon the actual value of data. Without condition variables, the programmer would need to have threads continually polling (possibly in a critical section), to check if the condition is met.

What are conditional variables in threads?

Condition variables are used to wait until a particular condition predicate becomes true. This condition predicate is set by another thread, usually the one that signals the condition.

What are condition variables in C?

A condition variable is an explicit queue that threads can put themselves on when some state of execution (i.e., some condition) is not as desired (by waiting on the condition); some other thread, when it changes said state, can then wake one (or more) of those waiting threads and thus allow them to continue (by sig- ...


Video Answer


1 Answers

Condition variables allow one to atomically release a held mutex and put the thread to sleep. Then, after being signaled, atomically re-acquire the mutex and wake up. You run into this, for example, in the producer/consumer problem. You will deadlock if you go to sleep while holding the mutex, but you could also deadlock if you release it before sleeping (by missing the signal to wake up).

It's not something that can be explained in a few paragraphs without examples, and there are several well-known pitfalls and caveats to using condition variables. Check out "An Introduction to Programming with Threads" by Andrew D. Birrell.

Regardless of the language, condition variables always take a mutex. The mutex must be held when wait is called. You should always verify that the desired condition is still true after returning from wait. That's why you always see conditional waits wrapped in a while loop. C++11 also gives you the predicate overload, which is syntactic sugar for the while loop.

The mutex protects the shared state. The condition lets you block until signaled.

unique_lock is an RAII (Resource Acquisition Is Initialization) wrapper for locking and unlocking the given mutex. It's conceptually identical to the lock statement in C#. It simplifies exception handling by tying the mutex acquisition and release to the lifetime of the unique_lock instance. I don't know if there's a reason why condition_variable forces you to use it other than the fact that it's good practice. The only difference between unique_lock and lock_guard is that unique_lock can be unlocked... which is why you have to use it instead of lock_guard with condition_variable.

like image 180
Humphrey Winnebago Avatar answered Oct 09 '22 23:10

Humphrey Winnebago