I found this stack-overflow question about this but it is 11 years old and I was wondering if there's a better way to do this as the question was posted in C++11 times.
There is std::thread::joinable()
but the problem is that even if the thread finished executing, it will still be marked as joinable
, if it hasn't been joined with std::thread::join()
.
A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable. docs
I could use an std::atomic<bool> threadFinished = false;
and set it to true
in the thread right before it finishes (this is mentioned in the answer of the stack-overflow question I mentioned at the beginning of this question).
Something like this:
std::atomic<bool> threadFinished = false;
std::thread myThread([&threadFinished]() {
std::this_thread::wait(1000);
threadFinished = true;
});
while (true) {
if (threadFinished) {
// Thread finished executing (note: myThread.joinable() is still true)
break;
}
}
myThread.join();
But this seems very unclean and unscalable, especially if you have many return points in the thread. Is there a better way to accomplish this?
But this seems very unclean and unscalable, especially if you have many return points in the thread. Is there a better way to accomplish this?
To fix the unclean problem: RAII
struct ThreadFinishedSignaler
{
~ThreadFinishedSignaler(){ threadFinished = true; }
}
then in the thread:
ThreadFinishedSignaler sig;
doWork();
if(something)
stopWorking(); // automatically sets true with destructor
doSomeWork();
// or here, automatically again, once the scope is gone
To fix the scalability: Have multiple atomics or mutexes working in parallel, 1 per thread:
struct NonFalseSharingMutexLock
{
std::mutex mut;
char augmentation[CacheLineSize - sizeof(std::mutex)];
}
then in the main thread:
NonFalseSharingMutexLock locks[numThreads];
finally in worker threads:
std::lock_guard<std::mutex> lg(locks[myThreadId]);
// change stuff
If main thread is not supposed to check an array of states, then you can combine the results as a "graph" with every 2 thread combining their results into 1 then other 2 threads do same, then 2 results combined into 1, ... like a graph or tree with no thread checking more than 2 results (less locking contention).
If the work done by each thread is big enough, you can simply use atomic integer:
In main thread:
std::atomic<int> numThreadsWorking = 32;
In worker threads:
struct ThreadFinishedSignaler
{
~ThreadFinishedSignaler(){ numThreadsWorking--; }
}
ThreadFinishedSignaler sig;
// exit anywhere, it just destructs and decrements automatically
when numThreadsWorking reaches zero, all work is complete.