I don't understand why when an std::thread
is destructed it must be in join() or detach() state.
Join waits for the thread to finish, and detach doesn't. It seems that there is some middle state which I'm not understanding. Because my understanding is that join and detach are complementary: if I don't call join() than detach() is the default.
Put it this way, let's say you're writing a program that creates a thread and only later in the life of this thread you call join(), so up until you call join the thread was basically running as if it was detached, no?
Logically detach() should be the default behavior for threads because that is the definition of what threads are, they are parallelly executed irrespective of other threads.
So when the thread object gets destructed why is terminate() called? Why can't the standard simply treat the thread as being detached?
I'm not understanding the rationale behind terminating a program when either join() or detached() wasn't called before the thread was destructed. What is the purpose of this?
UPDATE:
I recently came across this. Anthony Williams states in his book, Concurrency In Action, "One of the proposals for C++17 was for a joining_thread class that would be similar to std::thread, except that it would automatically join in the destructor much like scoped_thread does. This didn’t get consensus in the committee, so it wasn’t accepted into the standard (though it’s still on track for C++20 as std::jthread)..."
thread::detachSeparates the thread of execution from the thread object, allowing execution to continue independently. Any allocated resources will be freed once the thread exits.
If the program needs to know when that thread of execution has completed, some other mechanism needs to be used. join() cannot be called on that thread object any more, since it is no longer associated with a thread of execution. It is considered an error to destroy a C++ thread object while it is still "joinable".
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. But this some point is non-deterministic.
5.6 [ISO/IEC 9899:2011], states that a thread shall not be joined once it was previously joined or detached. Similarly, subclause 7.26. 5.3 states that a thread shall not be detached once it was previously joined or detached.
Technically the answer is "because the spec says so" but that is an obtuse answer. We can't read the designers' minds, but here are some issues that may have contributed:
With POSIX pthreads, child threads must be joined after they have exited, or else they continue to occupy system resources (like a process table entry in the kernel). This is done via pthread_join()
. Windows has a somewhat analogous issue if the process holds a HANDLE to the child thread; although Windows doesn't require a full join, the process must still call CloseHandle()
to release its refcount on the thread.
Since std::thread
is a cross-platform abstraction, it's constrained by the POSIX requirement which requires the join.
In theory the std::thread
destructor could have called pthread_join()
instead of throwing an exception, but that (subjectively) that may increase the risk of deadlock. Whereas a properly written program would know when to insert the join at a safe time.
See also:
You're getting confused because you're conflating the std::thread
object with the thread of execution it refers to. A std::thread
object is a C++ object (a bunch of bytes in memory) that acts as a reference to a thread of execution. When you call std::thread::detach
what happens is that the std::thread
object is "detached" from the thread of execution -- it no longer refers to (any) thread of execution, and the thread of execution continues running independently. But the std::thread
object still exists, until it is destroyed.
When a thread of execution completes, it stores its exit info into the std::thread
object that refers to it, if there is one (If it was detached, then there isn't one, so the exit info is just thrown away.) It has no other effect on the std::thread
object -- in particular the std::thread
object is not destroyed and continues to exist until someone else destroys it.
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