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