What is different between join() and detach() in multi threading in C++?
Does join() kill the thread?
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.
join() doesn't kill the thread. Actually it waits until thread main function returns. So if your thread main function looks like this:
while (true) {
}
join() is going to wait forever.
detatch() doesn't kill thread either. Actually it tells std::thread that this thread should continue to run even when std::thread object is destroyed. C++ checks in std::thread destructor that thread is either joined or detached and terminates program if this check fails.
So if you uncomment first line in main function of the following code it will crash. If you uncomment second or third line it will work ok.
#include <thread>
void func() {
}
void fail1() {
std::thread t(func);
// will fail when we try to destroy t since it is not joined or detached
}
void works1() {
std::thread t(func);
t.join();
}
void works2() {
std::thread t(func);
t.detach();
}
int main() {
// fail1();
// works1();
// works2();
}
By simple terms, both methods start a new thread but -
after detach() execution main thread continues running
after join() execution main thread pauses and waits until the new thread ends
by microsoft documentation
Enjoy!
Related
What is different between join() and detach() in multi threading in C++?
Does join() kill the thread?
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.
join() doesn't kill the thread. Actually it waits until thread main function returns. So if your thread main function looks like this:
while (true) {
}
join() is going to wait forever.
detatch() doesn't kill thread either. Actually it tells std::thread that this thread should continue to run even when std::thread object is destroyed. C++ checks in std::thread destructor that thread is either joined or detached and terminates program if this check fails.
So if you uncomment first line in main function of the following code it will crash. If you uncomment second or third line it will work ok.
#include <thread>
void func() {
}
void fail1() {
std::thread t(func);
// will fail when we try to destroy t since it is not joined or detached
}
void works1() {
std::thread t(func);
t.join();
}
void works2() {
std::thread t(func);
t.detach();
}
int main() {
// fail1();
// works1();
// works2();
}
By simple terms, both methods start a new thread but -
after detach() execution main thread continues running
after join() execution main thread pauses and waits until the new thread ends
by microsoft documentation
Enjoy!
Assume I'm starting a std::thread and then detach() it, so the thread continues executing even though the std::thread that once represented it, goes out of scope.
Assume further that the program does not have a reliable protocol for joining the detached thread1, so the detached thread still runs when main() exits.
I cannot find anything in the standard (more precisely, in the N3797 C++14 draft), which describes what should happen, neither 1.10 nor 30.3 contain pertinent wording.
1 Another, probably equivalent, question is: "can a detached thread ever be joined again", because whatever protocol you're inventing to join, the signalling part would have to be done while the thread was still running, and the OS scheduler might decide to put the thread to sleep for an hour just after signalling was performed with no way for the receiving end to reliably detect that the thread actually finished.
If running out of main() with detached threads running is undefined behaviour, then any use of std::thread::detach() is undefined behaviour unless the main thread never exits2.
Thus, running out of main() with detached threads running must have defined effects. The question is: where (in the C++ standard, not POSIX, not OS docs, ...) are those effects defined.
2 A detached thread cannot be joined (in the sense of std::thread::join()). You can wait for results from detached threads (e.g. via a future from std::packaged_task, or by a counting semaphore or a flag and a condition variable), but that doesn't guarantee that the thread has finished executing. Indeed, unless you put the signalling part into the destructor of the first automatic object of the thread, there will, in general, be code (destructors) that run after the signalling code. If the OS schedules the main thread to consume the result and exit before the detached thread finishes running said destructors, what will^Wis defined to happen?
The answer to the original question "what happens to a detached thread when main() exits" is:
It continues running (because the standard doesn't say it is stopped), and that's well-defined, as long as it touches neither (automatic|thread_local) variables of other threads nor static objects.
This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4 says as much, thanks to #dyp for the pointer).
Problems arise when the destruction of static objects has finished, because then execution enters a regime where only code allowed in signal handlers may execute ([basic.start.term]/1, 1st sentence). Of the C++ standard library, that is only the <atomic> library ([support.runtime]/9, 2nd sentence). In particular, that—in general—excludes condition_variable (it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>).
Unless you've unwound your stack at this point, it's hard to see how to avoid undefined behaviour.
The answer to the second question "can detached threads ever be joined again" is:
Yes, with the *_at_thread_exit family of functions (notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...).
As noted in footnote [2] of the question, signalling a condition variable or a semaphore or an atomic counter is not sufficient to join a detached thread (in the sense of ensuring that the end of its execution has-happened-before the receiving of said signalling by a waiting thread), because, in general, there will be more code executed after e.g. a notify_all() of a condition variable, in particular the destructors of automatic and thread-local objects.
Running the signalling as the last thing the thread does (after destructors of automatic and thread-local objects has-happened) is what the _at_thread_exit family of functions was designed for.
So, in order to avoid undefined behaviour in the absence of any implementation guarantees above what the standard requires, you need to (manually) join a detached thread with an _at_thread_exit function doing the signalling or make the detached thread execute only code that would be safe for a signal handler, too.
Detaching Threads
According to std::thread::detach:
Separates the thread of execution from the thread object, allowing
execution to continue independently. Any allocated resources will be
freed once the thread exits.
From pthread_detach:
The pthread_detach() function shall indicate to the implementation
that storage for the thread can be reclaimed when that thread
terminates. If thread has not terminated, pthread_detach() shall not
cause it to terminate. The effect of multiple pthread_detach() calls
on the same target thread is unspecified.
Detaching threads is mainly for saving resources, in case the application does not need to wait for a thread to finish (e.g. daemons, which must run until process termination):
To free the application side handle: One can let a std::thread object go out of scope without joining, what normally leads to a call to std::terminate() on destruction.
To allow the OS to cleanup the thread specific resources (TCB) automatically as soon as the thread exits, because we explicitly specified, that we aren't interested in joining the thread later on, thus, one cannot join an already detached thread.
Killing Threads
The behavior on process termination is the same as the one for the main thread, which could at least catch some signals. Whether or not other threads can handle signals is not that important, as one could join or terminate other threads within the main thread's signal handler invocation. (Related question)
As already stated, any thread, whether detached or not, will die with its process on most OSes. The process itself can be terminated by raising a signal, by calling exit() or by returning from the main function. However, C++11 cannot and does not try to define the exact behaviour of the underlying OS, whereas the developers of a Java VM can surely abstract such differences to some extent. AFAIK, exotic process and threading models are usually found on ancient platforms (to which C++11 probably won't be ported) and various embedded systems, which could have a special and/or limited language library implementation and also limited language support.
Thread Support
If threads aren't supported std::thread::get_id() should return an invalid id (default constructed std::thread::id) as there's a plain process, which does not need a thread object to run and the constructor of a std::thread should throw a std::system_error. This is how I understand C++11 in conjunction with today's OSes. If there's an OS with threading support, which doesn't spawn a main thread in its processes, let me know.
Controlling Threads
If one needs to keep control over a thread for proper shutdown, one can do that by using sync primitives and/or some sort of flags. However, In this case, setting a shutdown flag followed by a join is the way I prefer, since there's no point in increasing complexity by detaching threads, as the resources would be freed at the same time anyway, where the few bytes of the std::thread object vs. higher complexity and possibly more sync primitives should be acceptable.
Consider the following code:
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
void thread_fn() {
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout << "Inside thread function\n";
}
int main()
{
std::thread t1(thread_fn);
t1.detach();
return 0;
}
Running it on a Linux system, the message from the thread_fn is never printed. The OS indeed cleans up thread_fn() as soon as main() exits. Replacing t1.detach() with t1.join() always prints the message as expected.
The fate of the thread after the program exits is undefined behavior. But a modern operating system will clean up all threads created by the process on closing it.
When detaching an std::thread, these three conditions will continue to hold:
*this no longer owns any thread
joinable() will always equal to false
get_id() will equal std::thread::id()
When the main thread (that is, the thread that runs the main() function) terminates, then the process terminates and all other threads stop.
Reference: https://stackoverflow.com/a/4667273/2194843
To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3).
It's fine to use pthread_exit in main. When pthread_exit is used, the main thread will stop executing and will remain in zombie(defunct) status until all other threads exit.
If you are using pthread_exit in main thread, cannot get return status of other threads and cannot do clean-up for other threads (could be done using pthread_join(3)). Also, it's better to detach threads(pthread_detach(3)) so that thread resources are automatically released on thread termination. The shared resources will not be released until all threads exit.
When the main process terminates all the worker threads created by that process are also killed. So, if the main() returns before a detached thread it created completes execution the detached thread will be killed by OS. Take this example:
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout<<"Worker Thread Completed"<<endl;
}
int main(){
thread t(work);
t.detach();
cout<<"Main Returning..."<<endl;
return 0;
}
In the above program Worker Thread Completed will never be printed. Since main returns before the 2 second delay in the worker thread. Now if we change the code a little and add a delay greater than 2 seconds before main returns. Like:
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout<<"Worker Thread Completed"<<endl;
}
int main(){
thread t(work);
t.detach();
cout<<"Main Returning..."<<endl;
this_thread::sleep_for(chrono::seconds(4));
return 0;
}
Output
Main Returning...
Worker Thread Completed
Now if a thread is created from any functions other than main the detached thread will stay alive until it's executions has completed even after the function returns. For example:
void child()
{
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker Thread Completed" << endl;
}
void parent(){
thread t(child);
t.detach();
cout<<"Parent Returning...\n";
return;
}
int main()
{
parent();
cout<<"Main Waiting..."<<endl;
this_thread::sleep_for(chrono::seconds(5));
}
Output
Parent Returning...
Main Waiting...
Worker Thread Completed
A workaround to make main to wait for a detached worker thread before returning is to use condition_variable. For example:
#include <bits/stdc++.h>
using namespace std;
condition_variable cv;
mutex m;
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker Thread Completed" << endl;
cv.notify_all();
}
int main(){
thread t(work);
t.detach();
cout << "Main Returning..." << endl;
unique_lock<mutex>ul(m);
cv.wait(ul);
return 0;
}
I have the following class that has a std::thread as one of its member fields:
class MyClass {
private:
std::thread auxThread;
void run();
public:
~MyClass();
void start();
}
MyClass:~MyClass() {
if (auxThread.joinable())
auxThread.join();
}
void MyClass::run() {
//do stuff
}
void MyClass::start() {
auxThread = std::thread (&MyClass::run, this); //Move assignment
}
I can start the auxThread on-demand thanks to initializing it with an empty constructor and later move-assigning it to a std::thread object associated with an actual thread of execution (via the start() function), but to minimize system resource usage I'd like to join() auxThread with the main thread immediately after run() has exited i.e. when auxThread's work is done instead of in the MyClass destructor. It looks like condition_variable could be used to wake a sleeping main thread and accomplish this, but I don't want to block the main thread except (hopefully) briefly with join().
Two questions:
Is having a thread whose execution function has exited a drain on resources if it is never joined with the main thread, or is the thread and all associated resources released when the execution function exits (such that join() would presumably be unnecessary and return immediately)?
Is it possible to call join() on auxThread from the main thread in response to run() exiting without blocking the main thread?
Is having a thread whose execution function has exited a drain on resources if it is never joined with the main thread
Maybe. It depends on your implementation, but typically not.
is the thread and all associated resources released when the execution function exits
Probably, and if not, then as soon as possible by the OS. This is also implementation defined.
Is it possible to call join() on auxThread from the main thread in response to run() exiting without blocking the main thread?
Yes, but it wouldn't make any sense. The only thing that join does is block until the function being executed is done. Calling join again after run finished executing is unnecessary and basically a no-op.
Also, the "resources" from the thread are minimal. I wouldn't expect that a lot of memory would be allocated just for a single thread like yours. RAM is pretty cheap nowadays, so you shouldn't worry about that, as you are not executing 5M threads in parallel, which would make no sense on a conventional computer anyways.
Assume I'm starting a std::thread and then detach() it, so the thread continues executing even though the std::thread that once represented it, goes out of scope.
Assume further that the program does not have a reliable protocol for joining the detached thread1, so the detached thread still runs when main() exits.
I cannot find anything in the standard (more precisely, in the N3797 C++14 draft), which describes what should happen, neither 1.10 nor 30.3 contain pertinent wording.
1 Another, probably equivalent, question is: "can a detached thread ever be joined again", because whatever protocol you're inventing to join, the signalling part would have to be done while the thread was still running, and the OS scheduler might decide to put the thread to sleep for an hour just after signalling was performed with no way for the receiving end to reliably detect that the thread actually finished.
If running out of main() with detached threads running is undefined behaviour, then any use of std::thread::detach() is undefined behaviour unless the main thread never exits2.
Thus, running out of main() with detached threads running must have defined effects. The question is: where (in the C++ standard, not POSIX, not OS docs, ...) are those effects defined.
2 A detached thread cannot be joined (in the sense of std::thread::join()). You can wait for results from detached threads (e.g. via a future from std::packaged_task, or by a counting semaphore or a flag and a condition variable), but that doesn't guarantee that the thread has finished executing. Indeed, unless you put the signalling part into the destructor of the first automatic object of the thread, there will, in general, be code (destructors) that run after the signalling code. If the OS schedules the main thread to consume the result and exit before the detached thread finishes running said destructors, what will^Wis defined to happen?
The answer to the original question "what happens to a detached thread when main() exits" is:
It continues running (because the standard doesn't say it is stopped), and that's well-defined, as long as it touches neither (automatic|thread_local) variables of other threads nor static objects.
This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4 says as much, thanks to #dyp for the pointer).
Problems arise when the destruction of static objects has finished, because then execution enters a regime where only code allowed in signal handlers may execute ([basic.start.term]/1, 1st sentence). Of the C++ standard library, that is only the <atomic> library ([support.runtime]/9, 2nd sentence). In particular, that—in general—excludes condition_variable (it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>).
Unless you've unwound your stack at this point, it's hard to see how to avoid undefined behaviour.
The answer to the second question "can detached threads ever be joined again" is:
Yes, with the *_at_thread_exit family of functions (notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...).
As noted in footnote [2] of the question, signalling a condition variable or a semaphore or an atomic counter is not sufficient to join a detached thread (in the sense of ensuring that the end of its execution has-happened-before the receiving of said signalling by a waiting thread), because, in general, there will be more code executed after e.g. a notify_all() of a condition variable, in particular the destructors of automatic and thread-local objects.
Running the signalling as the last thing the thread does (after destructors of automatic and thread-local objects has-happened) is what the _at_thread_exit family of functions was designed for.
So, in order to avoid undefined behaviour in the absence of any implementation guarantees above what the standard requires, you need to (manually) join a detached thread with an _at_thread_exit function doing the signalling or make the detached thread execute only code that would be safe for a signal handler, too.
Detaching Threads
According to std::thread::detach:
Separates the thread of execution from the thread object, allowing
execution to continue independently. Any allocated resources will be
freed once the thread exits.
From pthread_detach:
The pthread_detach() function shall indicate to the implementation
that storage for the thread can be reclaimed when that thread
terminates. If thread has not terminated, pthread_detach() shall not
cause it to terminate. The effect of multiple pthread_detach() calls
on the same target thread is unspecified.
Detaching threads is mainly for saving resources, in case the application does not need to wait for a thread to finish (e.g. daemons, which must run until process termination):
To free the application side handle: One can let a std::thread object go out of scope without joining, what normally leads to a call to std::terminate() on destruction.
To allow the OS to cleanup the thread specific resources (TCB) automatically as soon as the thread exits, because we explicitly specified, that we aren't interested in joining the thread later on, thus, one cannot join an already detached thread.
Killing Threads
The behavior on process termination is the same as the one for the main thread, which could at least catch some signals. Whether or not other threads can handle signals is not that important, as one could join or terminate other threads within the main thread's signal handler invocation. (Related question)
As already stated, any thread, whether detached or not, will die with its process on most OSes. The process itself can be terminated by raising a signal, by calling exit() or by returning from the main function. However, C++11 cannot and does not try to define the exact behaviour of the underlying OS, whereas the developers of a Java VM can surely abstract such differences to some extent. AFAIK, exotic process and threading models are usually found on ancient platforms (to which C++11 probably won't be ported) and various embedded systems, which could have a special and/or limited language library implementation and also limited language support.
Thread Support
If threads aren't supported std::thread::get_id() should return an invalid id (default constructed std::thread::id) as there's a plain process, which does not need a thread object to run and the constructor of a std::thread should throw a std::system_error. This is how I understand C++11 in conjunction with today's OSes. If there's an OS with threading support, which doesn't spawn a main thread in its processes, let me know.
Controlling Threads
If one needs to keep control over a thread for proper shutdown, one can do that by using sync primitives and/or some sort of flags. However, In this case, setting a shutdown flag followed by a join is the way I prefer, since there's no point in increasing complexity by detaching threads, as the resources would be freed at the same time anyway, where the few bytes of the std::thread object vs. higher complexity and possibly more sync primitives should be acceptable.
Consider the following code:
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
void thread_fn() {
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout << "Inside thread function\n";
}
int main()
{
std::thread t1(thread_fn);
t1.detach();
return 0;
}
Running it on a Linux system, the message from the thread_fn is never printed. The OS indeed cleans up thread_fn() as soon as main() exits. Replacing t1.detach() with t1.join() always prints the message as expected.
The fate of the thread after the program exits is undefined behavior. But a modern operating system will clean up all threads created by the process on closing it.
When detaching an std::thread, these three conditions will continue to hold:
*this no longer owns any thread
joinable() will always equal to false
get_id() will equal std::thread::id()
When the main thread (that is, the thread that runs the main() function) terminates, then the process terminates and all other threads stop.
Reference: https://stackoverflow.com/a/4667273/2194843
To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3).
It's fine to use pthread_exit in main. When pthread_exit is used, the main thread will stop executing and will remain in zombie(defunct) status until all other threads exit.
If you are using pthread_exit in main thread, cannot get return status of other threads and cannot do clean-up for other threads (could be done using pthread_join(3)). Also, it's better to detach threads(pthread_detach(3)) so that thread resources are automatically released on thread termination. The shared resources will not be released until all threads exit.
When the main process terminates all the worker threads created by that process are also killed. So, if the main() returns before a detached thread it created completes execution the detached thread will be killed by OS. Take this example:
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout<<"Worker Thread Completed"<<endl;
}
int main(){
thread t(work);
t.detach();
cout<<"Main Returning..."<<endl;
return 0;
}
In the above program Worker Thread Completed will never be printed. Since main returns before the 2 second delay in the worker thread. Now if we change the code a little and add a delay greater than 2 seconds before main returns. Like:
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout<<"Worker Thread Completed"<<endl;
}
int main(){
thread t(work);
t.detach();
cout<<"Main Returning..."<<endl;
this_thread::sleep_for(chrono::seconds(4));
return 0;
}
Output
Main Returning...
Worker Thread Completed
Now if a thread is created from any functions other than main the detached thread will stay alive until it's executions has completed even after the function returns. For example:
void child()
{
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker Thread Completed" << endl;
}
void parent(){
thread t(child);
t.detach();
cout<<"Parent Returning...\n";
return;
}
int main()
{
parent();
cout<<"Main Waiting..."<<endl;
this_thread::sleep_for(chrono::seconds(5));
}
Output
Parent Returning...
Main Waiting...
Worker Thread Completed
A workaround to make main to wait for a detached worker thread before returning is to use condition_variable. For example:
#include <bits/stdc++.h>
using namespace std;
condition_variable cv;
mutex m;
void work(){
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker Thread Completed" << endl;
cv.notify_all();
}
int main(){
thread t(work);
t.detach();
cout << "Main Returning..." << endl;
unique_lock<mutex>ul(m);
cv.wait(ul);
return 0;
}
What is different between join() and detach() in multi threading in C++?
Does join() kill the thread?
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.
join() doesn't kill the thread. Actually it waits until thread main function returns. So if your thread main function looks like this:
while (true) {
}
join() is going to wait forever.
detatch() doesn't kill thread either. Actually it tells std::thread that this thread should continue to run even when std::thread object is destroyed. C++ checks in std::thread destructor that thread is either joined or detached and terminates program if this check fails.
So if you uncomment first line in main function of the following code it will crash. If you uncomment second or third line it will work ok.
#include <thread>
void func() {
}
void fail1() {
std::thread t(func);
// will fail when we try to destroy t since it is not joined or detached
}
void works1() {
std::thread t(func);
t.join();
}
void works2() {
std::thread t(func);
t.detach();
}
int main() {
// fail1();
// works1();
// works2();
}
By simple terms, both methods start a new thread but -
after detach() execution main thread continues running
after join() execution main thread pauses and waits until the new thread ends
by microsoft documentation
Enjoy!