What Is Different Between Join() and Detach() for Multi Threading in C++

What is different between join() and detach() for multi threading in C++?

A C++ thread object generally (but not always) represents a thread of execution, which is an OS or platform concept.

When thread::join() is called, the calling thread will block until the thread of execution has completed. Basically, this is one mechanism that can be used to know when a thread has finished. When thread::join() returns, the OS thread of execution has completed and the C++ thread object can be destroyed.

When the thread::detach() is called, the thread of execution is "detached" from the thread object and is no longer represented by a thread object - they are two independent things. The C++ thread object can be destroyed and the OS thread of execution can continue on. 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". That is, in order to destroy a C++ thread object either join() needs to be called (and completed) or detach() must be called. If a C++ thread object is still joinable when it's destroyed, an exception will be thrown.

Some other ways that a C++ thread object will not represent a thread of execution (ie., can be unjoinable):

  • A default constructed thread object does not represent a thread of execution, so is not joinable.
  • A thread that has been moved from will no longer represent a thread of execution, so is not joinable.

Why must one call join() or detach() before thread destruction?

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

When should I use std::thread::detach?

In the destructor of std::thread, std::terminate is called if:

  • the thread was not joined (with t.join())
  • and was not detached either (with t.detach())

Thus, you should always either join or detach a thread before the flows of execution reaches the destructor.


When a program terminates (ie, main returns) the remaining detached threads executing in the background are not waited upon; instead their execution is suspended and their thread-local objects destructed.

Crucially, this means that the stack of those threads is not unwound and thus some destructors are not executed. Depending on the actions those destructors were supposed to undertake, this might be as bad a situation as if the program had crashed or had been killed. Hopefully the OS will release the locks on files, etc... but you could have corrupted shared memory, half-written files, and the like.


So, should you use join or detach ?

  • Use join
  • Unless you need to have more flexibility AND are willing to provide a synchronization mechanism to wait for the thread completion on your own, in which case you may use detach

C++ : Can a thread be executed without calling the join() function in the main function?

Threads start execution not when .join() is called on them, but when they are constructed.

.join() is used to block the calling thread's execution until the thread being joined to has finished execution. It is typically required to join all threads before main() exits for the reason you observed:

When main() reaches the return statement, the scope of the std::thread objects is left. If neither .join() or .detach() has been called on the std::thread managing a thread at this point, the std::thread's destructor is going to call std::terminate() which (by default) aborts the whole program's execution.

Even if you detach the threads instead of joining them, when main() didn't block long enough via calls to .join() and exits before the threads have finished their output, there is a problem. After exiting main(), static storage duration objects are destroyed. One such object is std::cout. When the threads then try to output via std::cout after their wait, it is already destroyed and the program has undefined behavior.

Also note that detaching t1 and joining t2 isn't safe either, although the waiting times seem to suggest that. When exactly threads are scheduled for execution is up to the operating system. It could happen that thread t2 finishes before t1, in which case the issue mentioned above applies again, causing the program to again have undefined behavior.

Pthreads - Can I detach from a thread and then join in main?

Once detached, it's not possible to join anymore.

From Notes on pthread_detach()'s man page:

Once a thread has been detached, it can't be joined with
pthread_join(3) or be made joinable again.

Self terminating thread. Use join or detach

The correct way to terminate a thread from inside itself is to simply return from the function the thread is executing:

void ThreadFunction()
{
while (run_thread)
{
if (correct_message_found)
return;
else
ProcessMessage(); //example code to imitate network processing

//arbitrary wait. not relevant to the problem
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

Calling join from within the thread that is supposed to be joined is an error, see the first error condition: https://en.cppreference.com/w/cpp/thread/thread/join

join means "wait for the given thread to finish, then continue on". You are telling a thread to wait until it itself is finished. So it can only end once it has already ended, which is clearly contradictory.

Where you should call join is in the destructor of ThreadExample. ThreadFunction uses members of ThreadExample, and ThreadExample also owns the std::thread object, so ThreadExample cannot be allowed to die while the thread is still running. In the code you show, you would run into that problem if you input something before the thread is done: ThreadExample is then destroyed, and with it the std::thread object living inside. If a std::thread is destroyed while joinable (i.e. with a non-detached thread still running) then std::terminate is called:

https://en.cppreference.com/w/cpp/thread/thread/%7Ethread



Related Topics



Leave a reply



Submit