
Simple example of defining work as () operator overload on a struct.
#include <thread> #include <iostream> struct work{ void operator ()() const{ std::cout << "this is test from work" << std::endl; } }; int main(){ work w; std::thread t([&](){ w(); }); t.join(); // wait in the main thread for finishing the thread t. return 0; }
std::thread objects are not copyable but they are movable.
std::thread t1([]{std::cout << std::this_thread::get_id() << std::endl;}); std::thread t2 = t1; // NOT going to work since constructor for thread class is private std::thread t2 = std::move(t1); // this work!
Detach thread: background/long running jobs which does not to report the status back to parent threads can be detached.
t.detach(); // once detached, can not be joined.
RAII applied to thread class
#include <thread> #include <iostream> struct RThread{ RThread(){ std::thread t([](){std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl;}); t_= std::move(t); } // Rule of 5 ~RThread(){ if(t_.joinable()){ t_.join(); } } // delete the copy constructor and copy assignment operator RThread(RThread const &) = delete; RThread& operator=(RThread const &) = delete; // use default move operator and move assignment operator RThread(RThread const &&) = default; RThread & operator=(RThread const && ) = default; private: std::thread t_; }; int main(){ RThread(); return 0; }
Threads pool and Atomic shared variable
#include <thread> #include <iostream> #include <vector> #include <atomic> std::atomic<int> total(0); int main(){ std::vector<std::thread> workers; for(int i = 0; i <= 20; ++i){ workers.emplace_back( std::thread([](){ for(int j = 0; j <= 20; ++j){ // can also use ++total same effect // Memory order does not matter on x86 total.fetch_add(1, std::memory_order_relaxed); } })); } std::for_each( workers.begin(), workers.end(), [](auto & w){ if(w.joinable()){ w.join(); } } ); std::cout << total << std::endl; return 0; }
Using non-atomic variables with mutexes to prevent data races
#include <mutex> #include <thread> #include <iostream> #include <vector> static int total = 0; static int worker_count = 20; std::mutex mtx; int main(){ std::vector<std::thread> workers; workers.reserve(worker_count); for(int i = 0; i < worker_count; ++i){ workers.emplace_back(std::thread([](){ for(int j = 0; j < worker_count; ++j){ std::lock_guard<std::mutex> lkg(mtx); ++total; } }) ); } for(auto & worker: workers){ if (worker.joinable()){ worker.join(); } } std::cout << total << std::endl; return 0; }
DCLP – Double Checked Linked Pattern
As per the paper by Scott Meyers and Andrei Alexandrescu, the DCLP pattern implementation is given below.
In order to get instance of a singleton class, there are two checks, first check without the lock, then the lock is acquired and then again checked if the instance exists.
Singleton* Singleton::instance() { if (pInstance == 0) { // 1st test Lock lock; // acquire the lock if (pInstance == 0) { // 2nd test pInstance = new Singleton; } } return pInstance; }
In order to simplify this situation, c++ standard came with call_once and once_flag.
std::once_flag pInstance_initialized; // Flag std::call_once(pInstance_initialized, {pInstance =new Singleton;});
Here is an example code called once as per the once flag:
#include <thread> #include <iostream> #include <vector> #include <chrono> #include <atomic> int worker_count = 20; int main(){ std::vector<std::thread> workers; workers.reserve(worker_count); std::once_flag is_called; workers.reserve(worker_count); for(int i = 0; i < worker_count; ++i){ workers.emplace_back(std::thread([&](){ std::call_once(is_called, [](){ std::cout << "once_flag is set in the thread id " << std::this_thread::get_id() << std::endl; }); }) ); } for(auto & worker: workers){ if (worker.joinable()){ worker.join(); } } return 0; }
Condition Variables: used for intercommunication between the threads. Condition Variables work with mutex as a way of synchronization between threads.
Following is a classic example of printing number series like 0102030405… using 3 threads. One thread prints 0, another prints all odd numbers and 3rd thread prints even numbers. Condition variables are used to solve this problem.
#include <thread> #include <iostream> #include <mutex> #include <condition_variable> std::condition_variable cv; std::mutex mtx; int n = 1; int max = 100; bool digit_printed = false; int main(){ std::thread t_zero([](){ while(n < max){ std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [](){ return digit_printed == true; }); std::cout << 0<< "\n"; digit_printed = false; cv.notify_all(); } }); std::thread t_odd([](){ while(n < max){ std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [](){ return digit_printed == false && n%2 == 1; }); std::cout << n++ << "\n"; digit_printed = true; cv.notify_all(); } }); std::thread t_even([](){ while(n < max){ std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [](){ return digit_printed == false && n%2 == 0; }); std::cout << n++ << "\n"; digit_printed = true; cv.notify_all(); } }); t_zero.join(); t_odd.join(); t_even.join(); return 0; }