Basics

#include <iostream>
#include <thread>
#include <condition_variable>

using namespace std;

// Multithreading: good for I/O bound operations.
// Multiprocessing: good for CPU bound operations.

// function pointer.
void foo(int param) {
    cout << "function pointer: " << param << endl;
}

// lambda expression
auto f = [](int param) {
    cout << "function pointer: " << param << endl;
};

// Function object.
class fn_object_class {
    void operator() (int param) {
        cout << "function object: " << param << endl;
    }
};

class Base {
public:
  void foo(int param) {
    cout << "non-static: " << param << endl;
  }

  static void static_foo(int param) {
    cout << "static_foo: " << param << endl;
  }
};

// condition variables
// std::condition_variable is a synchronization primitive used with a std::mutex to block one 
// or more threads until another thread both modifies a shared variable(the condition) and notifies the std::condition_variable.

// The thread that intends to modify the shared variable must :

// Acquire a std::mutex(typically via std::lock_guard).
// Modify the shared variable while the lock is owned.
// Call notify_one or notify_all on the std::condition_variable(can be done after releasing the lock).
// Even if the shared variable is atomic, 
// it must be modified while owning the mutex to correctly publish the modification to the waiting thread.

// Any thread that intends to wait on a std::condition_variable must :

// Acquire a std::unique_lock<std::mutex> on the mutex used to protect the shared variable.
// Do one of the following :
// Check the condition, in case it was already updated and notified.
// Call wait, wait_for, or wait_until on the std::condition_variable(atomically releases the 
// mutex and suspends thread execution until the condition variable is notified, 
// a timeout expires, or a spurious wakeup occurs, then atomically acquires the mutex before returning).
// Check the condition and resume waiting if not satisfied.

// wait() - tells the current thread to wait till the condition variable is notified.
// wait_for() - tells the current thread to wait for some specific time duration. 
//              if notified early, the thread awakes.
// wait_until() - absolute time given instead of duration.
// notify_one() - notifies one of the waiting threads that shared resources is free to access.
//              thread selection is random.
// notify_all() - notifies all of the threads.

mutex mtx;
condition_variable cv;

bool data_ready = false;

void producer() {
  this_thread::sleep_for(chrono::seconds(2));
  // lock release 
  lock_guard<mutex> lock(mtx);
  // variable to avoid spurious wakeup 
  data_ready = true;
  // logging notification to console 
  cout << "Data Produced!" << endl;
  // notify consumer when done 
  cv.notify_one();
}

void consumer() {
  unique_lock<mutex> lock(mtx);
  cv.wait(lock, [] {
    return data_ready;
  });

  cout << "Data consumed!" << endl;
}

int main() {
    // // launching thread using function pointer.
    // thread thread_obj(foo, 2);

    // // launching thread using lambda expression.
    // thread thread_obj2(f, 2);

    // // launching thread using function object.
    // thread thread_obj3(fn_object_class(), 2);

    // // launching thread using non-static member function.
    // Base b;
    // thread thread_obj4(Base::foo, &b, 2);

    // // launching thread using static member function.
    // thread thread_obj5(&Base::foo, 2);

    // // Wait thread to finish.
    // thread_obj.join();

    thread consumer_t1(consumer);
    thread producer_t1(producer);

    consumer_t1.join();
    // producer_t1.join();

    return 0;
}```

Last updated