Check if an std::thread finished execution without std::thread::join()

101 Views Asked by At

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?

2

There are 2 best solutions below

0
huseyin tugrul buyukisik On BEST ANSWER

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.

0
The Dreams Wind On

The interface of std::future provides you with possibility to both obtain the result and checking if it's finished by inspecting results of wait member functions:

#include <iostream>
#include <chrono>
#include <thread>
#include <future>

int main()
{
    std::packaged_task<void()> task{ []() {
        for(auto i = 0; i < 16; ++i) {
            std::this_thread::sleep_for(std::chrono::milliseconds{ 256 });
        }
        std::cout << "The task is done!\n";
    } };
    auto future = task.get_future();
    std::thread{ std::move(task) }.detach();
    while(future.wait_for(std::chrono::milliseconds{ 512 }) != std::future_status::ready) {
        std::cout << "Waiting for the result...\n";
    }
    return EXIT_SUCCESS;
}