Sometimes it would be useful if a joinable std::thread
had the hability to execute thread::join()
on its destructor. See the examples below.
Example 1 (error):
The object std::thread
has been destroyed after the throw of the exception. Once the the flow exits the scope, the destructor is called BEFORE the join happens. It makes STL show an error message of 'abort'.
int main( int argc, const char * argv[] )
{
try
{
thread t( [] ()
{
this_thread::sleep_for( chrono::seconds( 1 ) );
cout << "thread done" << endl;
} );
throw exception( "some exception" );
t.join();
}
catch ( const exception & )
{
cout << "exception caught!" << endl;
}
cout << "main done" << endl;
return 0;
}
Example 2 (correct way):
The object t
is created before my try-catch block and the join() is put on both try and catch blocks. So it guarantees that the join() happens.
int main( int argc, const char * argv[] )
{
thread t;
try
{
t = thread( [] ()
{
this_thread::sleep_for( chrono::seconds( 1 ) );
cout << "thread done" << endl;
} );
throw exception( "some exception" );
t.join( );
}
catch ( const exception & )
{
t.join();
cout << "exception caught!" << endl;
}
cout << "main done" << endl;
return 0;
}
...AND THE QUESTION IS:
What is the reason for a joinable std::thread
not join automatically on its destructor?
It would be much easier if it happened automatically. The way it's done today requires one must be careful when using threads inside try-catch blocks, for example... but I am sure someone THOUGHT when designed std::thread
this way. So there must be a reason for that... what is that reason?
PS: I know we can envolve std::thread
in a class and put the join()
on the destructor of this new class... so it becomes automatic. But this is not the point. My question is really about std::thread
itself.
Another scenario is spawning helper threads when processing tasks. If you don't join these threads, you might end up using more resources than there are concurrent tasks, making it harder to measure the load. To be clear, if you don't call join , the thread will complete at some point anyway, it won't leak or anything.
A thread object is said to be joinable if it identifies/represent an active thread of execution. A thread is not joinable if: It was default-constructed. If either of its member join or detach has been called. It has been moved elsewhere.
When thread::join() returns, the OS thread of execution has completed and the C++ thread object can be destroyed.
The C++ thread join is used to blocks the threads until the first thread execution process is completed on which particular join() method is called to avoid the misconceptions or errors in the code.
The reason is simply so that you are forced to think about it. If a std::thread
object is destroyed due to an exception escaping the scope then a join may cause a blocking wait during stack unwinding, which is often undesirable, and can lead to deadlock if the thread that is being waited for is in turn waiting for some action on the part of the thread doing the waiting.
By having the application terminate in this situation you as a programmer are forced to actively think about the conditions that would cause the object to be destroyed, and ensure that the thread is joined correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With