Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ thread still `joinable()` after it finishes execution?

I have the following function:

void threadProc(){
    for (int i = 0; i < 5; ++i) {
        std::cout << "\n  thread #" << std::this_thread::get_id() << " says hi";
    }
    std::cout << "\n  Finished executing thread #" << std::this_thread::get_id();
}

And I am using it the following way:

int main(){
    try {
        std::thread t1(threadProc);
        t1.join();

        std::thread t2(threadProc);
        HANDLE handle = t2.native_handle(); 
        WaitForSingleObject(handle, INFINITE);
        std::this_thread::sleep_for(std::chrono::milliseconds(5000));
        std::cout << "\n  thread t2 is joinable: " << std::boolalpha << t2.joinable() << "\n\n";
    }
    catch (std::exception& ex){
        std::cout << "\n\n  " << ex.what() << "\n\n";
    }
    return 0;
}

This is the output:

thread #21300 says hi

thread #21300 says hi

thread #21300 says hi

thread #21300 says hi

thread #21300 says hi

Finished executing thread #21300

thread #2136 says hi

thread #2136 says hi

thread #2136 says hi

thread #2136 says hi

thread #2136 says hi

Finished executing thread #2136

thread t2 is joinable: true

And then it crashes when the try block goes out of scope because abort() was called on t2.

My question is, why is t2 still joinable() even when its threadProc was over? Why did it not finish processing?

Moreover, I am using WaitForSingleObject to ensure that I wait until t2 finishes processing. I also added the 5 seconds wait to make sure it takes its time to finish its processing. Yet something is still not done.

I know I can use t2.join() or t2.detach() but why do I have to? t2 has already finished processing (I think).


EDIT: I have tried the following code:

int main() {
    try {
        std::thread t1([]() {std::cout << "\n\n  Hi from thread #" << std::this_thread::get_id(); });
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    catch (std::exception& ex) {
        std::cout << "\n\n  " << ex.what() << "\n\n";
    }
    return 0;
}

And still the thread is joinable. I looked up the joinable reference and they say:

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.

So this isn't related to WaitForSingleObject anymore. The question is why a thread is still considered joinable() after it finished execution?

I have seen this question which confused me even more as it states that when the thread finished execution it was not joinable() even before calling join() or detach().

like image 744
Everyone Avatar asked Mar 09 '17 10:03

Everyone


2 Answers

The question is why a thread is still considered joinable() after it finished execution?

Because you might want to write code that join()s it. If t.joinable() automatically became false upon termination, then there would be no safe way to call t.join(). You could write this:

if (t.joinable()) {
    t.join();
}

But that still could throw an exception if the thread terminated after t.joinable() had returned true, but before the caller was able to complete the t.join() call.

Having the thread remain joinable until it actually is join()ed is simpler behavior to describe, and it's easier to write code that uses it correctly.

like image 161
Solomon Slow Avatar answered Oct 06 '22 07:10

Solomon Slow


joinable doesn't mean still_running , joinable simply means the thread object is not "empty" (as result of default construction or move) and is associated with a real OS-thread of execution.

if the thread is empty, that means there is nothing to join, hence, the thread is not "joinable".

if the thread is not empty, you can make the current thread wait for it, hence it is "joinable".

like image 30
David Haim Avatar answered Oct 06 '22 08:10

David Haim