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:
- https://en.wikipedia.org/wiki/Zombie_process
- https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
- https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process