Condition Variable(Multithreading) C++ Introduction














































Condition Variable(Multithreading) C++ Introduction



Introduction On Condition Variable(CV)

Condition Variable is a kind of Event used for signaling between two or more threads. One or more thread can wait on it to get signaled, while an another thread can signal this.

Used for 2 purposes:
  • Notify other threads.
  • Waiting for some conditions.

It uses a unique_lock(over a mutex) to lock the thread when one of its wait functions is called. The thread remains blocked until woken up by another thread that calls a notification function on the same condition_variable object.

CV allows running threads to wait on some conditions and once those conditions are met,the waiting thread is notified using::
  1. notify_one();
  2. notify_all();  
We need mutex to use condition variable(obviously).

If some thread want to wait on some conditions then it has to do these steps:

  1. Acquire the mutex lock using std::unique_lock<std::mutex>lock(m);.
  2. Execute wait,wait_for,or wait_until.The wait operations atomically release the mutex and suspend the execution of the thread.
  3. When the condition variable is notified,the thread is awakened ,and the mutex is atomically reacquired.
  4. The thread should then check the conditons and resume waiting if the wake up was spurious.

NOTE
  • CV are used to synchronise two or more threads.
  • Best use of CV is Producer/Consumer Problem.

Member functions

(constructor)            constructs the object
(destructor)              destructs the object
operator=[deleted]   not copy-assignable

Notification   

notify_one          notifies one waiting thread
notify_all            notifies all waiting threads

Waiting

wait           blocks the current thread until the condition variable is woken up
wait_for     blocks the current thread until the condition variable is woken up or after the specified timeout 
duration

wait_until       blocks the current thread until the condition variable is woken up or until specified time point has been reached

Native handle

native_handle     returns the native handle.

A Sample Program

#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;
}



Comments