Table of Contents

C - C++ Threads - Event Handling

To have a thread wait for an event to happen like a condition to become true or a task to be completed by another thread.


Option 1 - Use a boolean global variable

Two threads:

  1. thread1 performs some actions, then sets a flag.
  2. thread2 is waiting for this flag to be set.
#include<iostream>
#include<thread>
#include<mutex>
 
class Application
{
 std::mutex m_mutex;
 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;
  }
 
  void mainTask()
  {
    std::cout<<"Do Some Handshaking"<<std::endl;
 
    // Acquire the Lock.
    m_mutex.lock();
 
    // Check if flag is set to true or not.
    while(m_bDataLoaded != true)
    {
      // Release the lock.
      m_mutex.unlock();
 
      // Sleep for 100 milliseconds.
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
 
      // Acquire the lock
      m_mutex.lock();
    }
 
    // Release the lock.
    m_mutex.unlock();
 
    // Doc processing on loaded Data.
    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;
}

NOTE: It has following disadvantages:

  • Thread will keep on acquiring the lock and releasing it just to check the value, therefore it will consume CPU cycles and will also make Thread 1 slow, because it needs to acquire same lock to update the bool flag.

A better choice is to use Condition Variables; which 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 it.


Option 2 - Using a Condition Variable

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