203

Sometime I have to use std::thread to speed up my application. I also know join() waits until a thread completes. This is easy to understand, but what's the difference between calling detach() and not calling it?

I thought that without detach(), the thread's method will work using a thread independently.

Not detaching:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called without detach");
    });

    //some code here
}

Calling with detaching:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called with detach");
    });

    t.detach();

    //some code here
}
Jeffmagma
  • 452
  • 2
  • 8
  • 20
Jinbom Heo
  • 7,248
  • 14
  • 52
  • 59

6 Answers6

225

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 are not 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
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • If I would call pthread_exit(NULL); in main() then exit() wouldn't be called from main() and hence program will continue execution until all detached threads would complete. Then exit() would be called. – Kurovsky Jun 12 '15 at 11:21
  • 2
    @Matthieu , why can't we join in the destructor of std::thread ? – john smith Aug 29 '16 at 22:45
  • 2
    @johnsmith: An excellent question! What happens when you join? You wait until the thread complete. If an exception is thrown, destructors are executed... and suddenly the propagation of your exception is suspended until the thread terminates. There are many reasons for it not to, notably if it is waiting on input from the currently suspended thread! So the designers chose to make it an explicit choice, rather than pick a controversial default. – Matthieu M. Aug 29 '16 at 23:20
  • @Matthieu I think you mean call join() before std::thread's destructor is reached. You can (and should?) join() the the destructor of an enclosing class? – Jose Quinteiro Nov 23 '16 at 17:59
  • 6
    @JoseQuinteiro: Actually, unlike other resources, it is advised *not* to join from a destructor. The problem is that joining does *not end* a thread, it merely *waits* for it to end, and unlike you have a signal in place to cause the thread to terminate you might be waiting for a long time... *blocking* the current thread whose stack is being unwound and preventing *this current thread* from ever terminating thus blocking the thread waiting for it, etc... So, unless you are *certain* that you can stop a given thread in a reasonable amount of time, it is best not to wait for it in a destructor. – Matthieu M. Nov 23 '16 at 19:05
  • *"instead their execution is suspended and their thread-local objects destructed"* - this is not true, only thread-local objects from the thread that causes process to end are destroyed, any remaining running threads are then silently killed by the system without any cleanup. And these remaining running threads may continue using objects with static storage duration while those objects are being destroyed. – user7860670 Mar 24 '23 at 12:27
  • @user7860670: Indeed! Not sure how it's gone so long without anyone noticing. Thanks for bringing it to my attention. – Matthieu M. Mar 24 '23 at 13:17
  • The *"their execution is suspended"* part is not correct either, they continue running and may use objects with static storage duration while/after those objects are destructed. I don't think that suspending threads even available on some platforms. – user7860670 Mar 24 '23 at 19:56
  • @user7860670 I see what you mean, but explaining the nuance is a lot more complicated. The threads _are_ suspended at some point -- in that the OS stops scheduling them. They may not be all suspended at the same time, and some may still be running while cleanup runs, but the OS will get to them all. – Matthieu M. Mar 25 '23 at 11:26
48

You should call detach if you're not going to wait for the thread to complete with join but the thread instead will just keep running until it's done and then terminate without having the spawner thread waiting for it specifically; e.g.

std::thread(func).detach(); // It's done when it's done

detach basically will release the resources needed to be able to implement join.

It is a fatal error if a thread object ends its life and neither join nor detach has been called; in this case terminate is invoked.

6502
  • 112,025
  • 15
  • 165
  • 265
40

This answer is aimed at answering question in the title, rather than explaining the difference between join and detach. So when should std::thread::detach be used?

In properly maintained C++ code std::thread::detach should not be used at all. Programmer must ensure that all the created threads gracefully exit releasing all the acquired resources and performing other necessary cleanup actions. This implies that giving up ownership of threads by invoking detach is not an option and therefore join should be used in all scenarios.

However some applications rely on old and often not well designed and supported APIs that may contain indefinitely blocking functions. Moving invocations of these functions into a dedicated thread to avoid blocking other stuff is a common practice. There is no way to make such a thread to exit gracefully so use of join will just lead to primary thread blocking. That's a situation when using detach would be a less evil alternative to, say, allocating thread object with dynamic storage duration and then purposely leaking it.

#include <LegacyApi.hpp>
#include <thread>

auto LegacyApiThreadEntry(void)
{
    auto result{NastyBlockingFunction()};
    // do something...
}

int main()
{
    ::std::thread legacy_api_thread{&LegacyApiThreadEntry};
    // do something...
    legacy_api_thread.detach();
    return 0;
}
devoured elysium
  • 101,373
  • 131
  • 340
  • 557
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • I wouldn't say this only applies to legacy APIs, e.g. `std::getline` will block indefinitely – Hugo Burd Aug 11 '20 at 19:01
  • "std::thread::detach should not be used at all" this is simply not true. If you have a thread that is supposed to live until the end of the program, you can detach it and in fact should detach it if you can't guarantee its completion, for example if thread contains a signal handler and will sometimes call `exit` instead of returning. Joining thread that never returns will lock your program. – Reverent Lapwing Mar 23 '23 at 18:45
  • @ReverentLapwing I think you should read my answer past that sentence. I've mentioned a possible valid reason to detach a thread. And i should add that 1) there is no problem with graceful exit when thread contains a signal handler (if we are talking about posix signals) 2) invoking `exit` anywhere in the application is even worse practice than invoking `detach` since no destructors will be invoked and no cleanup performed. – user7860670 Mar 23 '23 at 19:02
  • @user7860670 your possible valid reason to detach is "using not well designed APIs" and you say detach is never used in "properly maintained code". I take issue with that. Detached thread will release all its resources upon its exit, detaching is equivalent to thread calling join on itself when finished. As a programmer, your only task is making sure the thread will exit before the main thread, which is exactly the same requirement as when using join. If you set std::terminate_handler, you don't even need to worry about returning before main. Detached thread is no more incorrect than pointers. – Reverent Lapwing Mar 24 '23 at 10:38
  • @ReverentLapwing *"detaching is equivalent to thread calling join on itself when finished"* - This is not true. If program ends, by returning from `main` for example, then variables with static and thread local (for main thread only) storage duration will be destroyed and any remaining running threads will be silently killed by the system without performing any cleanup. *"your only task is making sure the thread will exit before the main thread"* - yes, and *the only* way to ensure this is by invoking `join`. – user7860670 Mar 24 '23 at 12:11
  • @user7860670 How is that not true? If the program ends before the thread, then the thread didn't finish. And join is not the only way to ensure order: mutexes, atomic counters and even sleep can all be used to ensure order depending on what the thread actually does. If it's unreasonable to assume that the thread will last longer than main thread (thread will return in a second), then you can usually ensure this by doing nothing. Detached thread have advantage of cleaning after itself instead of waiting with cleanup for join. – Reverent Lapwing Mar 24 '23 at 14:04
  • @ReverentLapwing If program ends then any remaining threads are going to be silently killed by the system without any cleanup. here is [an example](https://godbolt.org/z/x1KqePf1r) or [another example](https://godbolt.org/z/8sGfdsPhj) As for making custom means to make sure that thread is finished running: while it is indeed trivial to add some flag at the end of thread function to signal that it is done, however in thread will continue running past that point - destructors of remaining thread local objects and other cleanup actions will be performed. – user7860670 Mar 24 '23 at 19:53
  • Things like mutexes, atomic counters and even sleep won't provide a strong guarantee that thread has actually finished running, only `join` does. Note: you can find reference in the C++ standard in **[basic.start]** section. – user7860670 Mar 24 '23 at 19:53
  • But [this answer](https://stackoverflow.com/a/38819432/10027592) and its comments say that OS(at least Linux and Windows) cleans up the `detach`ed thread when `main` finishes. Could you elaborate on this? – starriet May 09 '23 at 03:16
  • 1
    @starriet System does cleanup process memory and opened file handles upon process exiting. However no user-specified cleanup actions are performed. Basically detached threads are killed in the middle of whatever they were doing. For example, if detach thread has been writing into some buffered file stream then it won't be flushed and the accumulated data will be lost. – user7860670 May 09 '23 at 10:04
  • @user7860670 Thanks! So, using `detach` and finishing the main function is just shutting down everything, though the detached threads are shut down by the OS(not by user-specified cleanup code), right? Then, what's the point of `detach` in the example code? The OS will kill the detached thread possibly before `NastyBlockingFunction()` returns. – starriet May 09 '23 at 20:47
  • 1
    @starriet `detach` is used here because invoking `join` will cause process to hang indefinitely until `NastyBlockingFunction` returns while leaving thread in joinable state will cause thread destructor invoke `std::terminate` crashing the process. – user7860670 May 11 '23 at 07:35
  • @user7860670 Thank you so much. Then, can I think of `detach` as a way of finishing the `main` function without waiting for the possibly blocked worker thread, letting the OS kill that thread(even if that worker thread hasn't done its job yet)? And that's why properly maintained C++ code should not use `std::thread::detach`? – starriet May 12 '23 at 04:28
16

When you detach thread it means that you don't have to join() it before exiting main().

Thread library will actually wait for each such thread below-main, but you should not care about it.

detach() is mainly useful when you have a task that has to be done in background, but you don't care about its execution. This is usually a case for some libraries. They may silently create a background worker thread and detach it so you won't even notice it.

GreenScape
  • 7,191
  • 2
  • 34
  • 64
2

According to cppreference.com:

Separates the thread of execution from the thread object, allowing execution to continue independently. Any allocated resources will be freed once the thread exits.

After calling detach *this no longer owns any thread.

For example:

  std::thread my_thread([&](){XXXX});
  my_thread.detach();

Notice the local variable: my_thread, while the lifetime of my_thread is over, the destructor of std::thread will be called, and std::terminate() will be called within the destructor.

But if you use detach(), you should not use my_thread anymore, even if the lifetime of my_thread is over, nothing will happen to the new thread.

DinoStray
  • 696
  • 1
  • 6
  • 20
  • OK, I take back what I said just now.@TobySpeight – DinoStray Jul 17 '18 at 09:55
  • 8
    Note that if you use `&` in the lambda capture you're using the enclosing scope's variables by **reference** - so you better be sure the lifetime of any you reference is longer than your thread lifetime. – DavidJ Feb 21 '19 at 19:16
  • 1
    @DavidJ using `[=]` may possibly solve that problem. –  Oct 06 '21 at 10:28
1

Maybe it is good idea to iterate what was mentioned in one of the answers above: When the main function is finished and main thread is closing, all spawn threads either will be terminated or suspended. So, if you are relying on detach to have a background thread continue running after the main thread is shutdown, you are in for a surprise. To see the effect try the following. If you uncomment the last sleep call, then the output file will be created and written to fine. Otherwise not:

#include <mutex>
#include <thread>
#include <iostream>
#include <fstream>
#include <array>
#include <chrono>

using Ms = std::chrono::milliseconds;

std::once_flag oflag;
std::mutex mx;
std::mutex printMx;
int globalCount{};
std::ofstream *logfile;
void do_one_time_task() {
    //printMx.lock();
    //std::cout<<"I am in thread with thread id: "<< std::this_thread::get_id() << std::endl;
    //printMx.unlock();
    std::call_once(oflag, [&]() {
    //  std::cout << "Called once by thread: " << std::this_thread::get_id() << std::endl; 
    //  std::cout<<"Initialized globalCount to 3\n";
        globalCount = 3;
        logfile = new std::ofstream("testlog.txt");
        //logfile.open("testlog.txt");
        });
    std::this_thread::sleep_for(Ms(100));
    // some more here
    for(int i=0; i<10; ++i){    
        mx.lock();
        ++globalCount;
        *logfile << "thread: "<< std::this_thread::get_id() <<", globalCount = " << globalCount << std::endl;
        std::this_thread::sleep_for(Ms(50));
        mx.unlock();
        std::this_thread::sleep_for(Ms(2));
    }

    std::this_thread::sleep_for(Ms(2000));
    std::call_once(oflag, [&]() {
        //std::cout << "Called once by thread: " << std::this_thread::get_id() << std::endl;
        //std::cout << "closing logfile:\n";
        logfile->close();
        });

}

int main()
{
    std::array<std::thread, 5> thArray;
    for (int i = 0; i < 5; ++i)
        thArray[i] = std::thread(do_one_time_task);

    for (int i = 0; i < 5; ++i)
        thArray[i].detach();

    //std::this_thread::sleep_for(Ms(5000));
    std::cout << "Main: globalCount = " << globalCount << std::endl;

    return 0;
}
rezeli
  • 143
  • 2