1

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?

12
  • 1
    "But this seems very unclean and unscalable [...]" - in this case I would inherit from std::jthread privately and add the functionality you desire to be handled internally by your class. Commented Jul 22, 2023 at 13:55
  • So there isn't any built-in functionality for this? Commented Jul 22, 2023 at 13:57
  • Why do you need that? If you want to wait until the thread is finished, then just join it, that's exactly what join does. Commented Jul 22, 2023 at 13:59
  • I think the value returned by std::thread::get_id might give you that information. Worth trying that, at any rate. Commented Jul 22, 2023 at 14:01
  • 2
    I fail to see what concern you're trying to address. Your thread function is (atomically) setting threadFinished to true immediately before it terminates. Your other thread is waiting for threadFinished to be true and then calling myThread.join(). Since myThread.join() will wait for myThread to complete, all of your machinery is just adding additional complication but achieving no more. Commented Jul 22, 2023 at 14:06

2 Answers 2

2

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;
}
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.