Concurrency and parallelism
CP.25
Prefer `gsl::joining_thread` over `std::thread`
Reason
A joining_thread is a thread that joins at the end of its scope. Detached threads are hard to monitor. It is harder to ensure absence of errors in detached threads (and potentially detached threads).
Example, bad
void f() { std::cout << "Hello "; }
struct F {
void operator()() const { std::cout << "parallel world "; }
};
int main()
{
std::thread t1{f}; // f() executes in separate thread
std::thread t2{F()}; // F()() executes in separate thread
} // spot the bugs
Example
void f() { std::cout << "Hello "; }
struct F {
void operator()() const { std::cout << "parallel world "; }
};
int main()
{
std::thread t1{f}; // f() executes in separate thread
std::thread t2{F()}; // F()() executes in separate thread
t1.join();
t2.join();
} // one bad bug left
Note
Make "immortal threads" globals, put them in an enclosing scope, or put them on the free store rather than detach(). Don't detach.
Note
Because of old code and third party libraries using std::thread, this rule can be hard to introduce.
Enforcement
Flag uses of std::thread:
- Suggest use of
gsl::joining_threador C++20std::jthread. - Suggest "exporting ownership" to an enclosing scope if it detaches.
- Warn if it is not obvious whether a thread joins or detaches.