Concurrency
Threads
Include
#include <thread>
Run Function
void run() {
printf("Hello World!");
}
std::thread t(run);
t.join();
Run Class
class Thread {
public:
void operator()() {
printf("Hello World!");
}
}
Thread thread;
std::thread t(thread);
Run Lambda
std::thread([]{
printf("Hello World!");
});
Print Id
std::this_thread::get_id();
Join
Synchronization point between caller and called thread.
thread.joinable(); // true
thread.join();
thread.joinable(); // false
Detach
thread.detach();
Sleep
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
Yield
Give up current time slice and allow other threads to run.
std::this_thread::yield();
Get Number of Cores
std::thread::hardware_concurrency();
RAII Thread
Resource acquisition is initialization (Constructor acquire resources destructor releases resources)
class RAIIThread() {
public:
explicit RAIIThread(std::thread &t : t_(t)){}
~RAIIThread() {
if (t.joinable()) {
t.join();
}
}
RAIIThread(RAIIThread &const) = delete; // Remove copy constructor
RAIIThread &operator= (RAIIThread& const) = delete; // Remove copy assignment operator
private:
std::thread &t_;
}
Pass Parameters
- By Value
void run(int x, int y) {
}
int p = 1;
int q = 2;
std::thread(run, p, q);
- By Reference
void run(int &x) {
}
int q = 2;
std::thread(run, std::ref(p));
Locks
Mutex
#include <mutex>
std::mutex m;
m.lock();
// access resource
m.unlock();
Lock Guard
void run() {
std::lock_guard<std::mutex> lg(m);
// access resource
}
Unique Lock
Like lock guard but doesnt have to lock in the constructor.
void run() {
std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
std::unique_lock<std::mutex> lock2(m2, std::defer_lock);
std::lock(lock1, lock2); // aquire both or wait
// access resource
}
Thread-Safe Stack
template<typename T>
ConcurrentStack {
public:
void push(T element) {
std::lock_guard<std::mutex> lg(mutex_);
stack_.push(std::make_shared<T>(element));
}
std::shared_ptr<T> pop() {
std::lock_guard<std::mutex> lg(mutex_);
if (stack_.empty()) {
throw std::runtime_error("Stack is empty");
}
std::shared_ptr<T> result(stack_.top());
stack_.pop();
return result;
}
bool empty() {
std::lock_guard<std::mutex> lg(mutex_);
return stack_.empty();
}
size_t size() {
std::lock_guard<std::mutex> lg(mutex_);
return stack_.size();
}
private:
std::stack<std::shared_ptr<T>> stack_;
std::mutex mutex_;
}
Condition Variables
Lets the thread wake up when an event happens. If a thread determined that an event is satisfied it can notify one or more threads who are waiting on this condition.
#include <conditional_variable>
int total_distance = 10;
int distance_covered = 0;
std::conditional_variable cv;
std::mutex m;
void keep_moving() {
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
distance_covered++;
if (distance_covered == total_distance) {
cv.notify_one();
}
}
}
void wait_to_arrive() {
std::unique_lock<std::mutex> ul(m);
cv.wait(ul, []{ distance_covered == total_distance; });
// event occured
}
Futures
#include <future>
int find_answer() {
return 5000;
}
std::future<int> the_answer = std::async(std::launch::async, find_answer)
the_answer.get();
Run policies:
std::launch::asyncrun on separate threadstd::launch::deferredrun on current threadstd::launch::async | std::launch::deferredcompiler can decide where to run