void func1() { … }
int main() {
std::thread t1(func1);
// t1.join(); // main thread waits for t1 to finish
t1.detach() // t1 runs freely -> daemon process => C++ lib is responsible to reclaim resources
// once detach, cannot join again!
// t1.joinable() -> check if t1 is joinable
}
std::thread can also be constructed with any callable object, for example functor:
class Functor {
public:
void operator(string &msg)() {
// …
}
};
int main() {
std::string s = “hello”;
// need extra parentheses to avoid “Functor()”
// being interpreted as function call
// std::thread t1((Functor())); // if operator() doesn’t take any param
// s is being passed by value because param to threads
// are always pass by value!!
// std::thread t1((Functor()), s); => copying!
// need to use ref wrapper to pass by ref
std::thread t1((Functor()), std::ref(s));
t1.join();
}
std::thread is a move-only object, cannot be copied!
Oversubscription: creating more threads than CPU can support!
std::thread::hardware_concurrency() returns the number of concurrent thread supported.
std::thread memory for each thread is managed by POSIX native thread library…
same behavior with std::thread but auto join when destroyed
Race condition is when the result of a program depends on the order of execution of threads.
std::mutex mu;
// unsafe usage:
// mu.lock();
// // do something => but if exception raises here, mutex is locked forever!
// mu.unlock();
// solution: use lock guard
{ // put within scope, mutex unlocked whenever lock guard is out of scope
std::lock_guard<std::mutex> guard(mu); // RAII
// do something
}
std::mutex implements 2 phases: