C++

How do you detach a Thread in C++?

A thread detaches from what? – A thread detaches from a join. The next question is “what is a join?” – Instead of having a program of statements running from the beginning to the end, sequentially, the program can be grouped into special sections of statements. The special sections are called threads, which can then run in parallel or concurrently. To convert a set of statements into a thread, special coding is needed. Unfortunately, threads in C++ would run independently if they are not joined. In such a situation, a second thread may end after the main thread has ended. This is usually not desirable.

For there to be any joining, two threads are needed. One thread calls the other thread. Joining a thread means that, while the calling thread is running, it would halt at a position and wait for the called thread to complete its execution (to its end), before it continues its own execution. At the position where the thread halts, there is a join expression. Such halting is called blocking.

If the called thread is taking too long to complete and has probably done what the calling thread expected it to do, then the calling thread can detach it. After detaching, if the called thread completes after the calling thread, there should be no problem. Detaching means breaking the join (link).

Recall

A thread is a top-level function that has been enclosed into a thread object, instantiated from the thread class. Instantiating the thread with the top-level function means calling the function. Here is a simple thread program, with the join statement:

#include <iostream>

#include <thread>

using namespace std;


void func() {

        cout <<". . . from thread!" <<'\n';

    }  


int main()

{

    thread thd(func);

    thd.join();

    /* statements */


    return 0;

}

There are two threads here: the object, thd, and the main() function. The main function is like the main thread. Note the inclusion of the thread library. The output is:

    . . . from thread!

At the command prompt, a C++20 program with threads, should be commanded as follows, for the g++ compiler :

    g++ -std=c++2a sample.cc -lpthread -o sample.exe

Article Content

detach() Syntax

The detach() syntax is simple; it is:

    threadObject.detach()

This member function of the thread object returns void. threadObject is the thread object of the thread whose function is running. When the function of a thread is running, the thread is called the executing thread.

A thread can only be detached after it has been joined; otherwise, the thread is already in the detached state.

Ambiguity of Detaching in the Calling Thread’s Body

In the following program, the called thread is detached in the body of the calling thread:

    #include <iostream>

    #include <thread>

    #include <string>

    using namespace std;


    string globl = string("on earth!");


    void func(string st) {

        string fin = "Living " + st;

        cout <<fin <<endl;

    }  


    int main()

    {

        thread thr(func, globl);

        thr.join();

        thr.detach();


        return 0;

    }

The output from the author’s computer at runtime was:

    Living on earth!

    terminate called after throwing an instance of 'std::system_error'

      what():  Invalid argument

    Aborted (core dumped)

The proper output expected should be just:

    Living on earth!

When a thread ends its execution, the implementation releases all the resources that it owned. When a thread is joined, the body of the calling thread waits at that point until the called thread completes its execution, then the body of the calling thread continues its own execution.

The problem of the presence of the further output is that though the called thread might have completed its task given to it, its resources had not all been taken away, but the detach() function caused the body of the calling function to continue executing. In the absence of the detach() function, the called thread would have completed, plus all its resources taken away; and the output would have been the simple one-line expected.

To convince the reader further, consider the following program, which is the same as the above, but with the join() and detach() statements commented out:

    #include <iostream>

    #include <thread>

    #include <string>

    using namespace std;


    string globl = string("on earth!");


    void func(string st) {

        string fin = "Living " + st;

        cout <<fin <<endl;

    }  


    int main()

    {

        thread thr(func, globl);

        //thr.join();

        //thr.detach();


        return 0;

    }

The output from the author’s computer is:

    terminate called without an active exception

    Aborted (core dumped)

The main() function ran through to its end without waiting for the thread to do anything. And so, the thread could not display its output.

Thread Name at Global Scope

A thread can be instantiated at global scope. The following program illustrates this:

    #include <iostream>

    #include <thread>

    using namespace std;


    thread thr;


    void func() {

        cout <<"the first line" <<endl;

        cout <<"the second line" <<endl;

    }


    int main()

    {

        thr = thread(func);

        thr.join();


        return 0;

    }

The output is:

    the first line

    the second line

Before the function, func() is defined in the program; there is the statement,

    thread thr;

which instantiates the thread, thr. At this point, thr does not have a corresponding function. In the main() function, the first statement is:

        thr = thread(func);

The right-hand side of this statement creates a thread without a name and assigns the thread to the thread variable, thr. In this way, thr acquires a function. The next statement joins the called thread.

Detaching within the Called Thread

A better way to detach a thread is to do so within the body of the called thread. In this case, the thread object would have to be created in the global scope, as illustrated above. Then the detach statement will be in the body of the called thread, where detaching should take place. The following program illustrates this:

    #include <iostream>

    #include <thread>

    using namespace std;


    thread thr;


    void func() {

        cout <<"the first line" <<endl;

        thr.detach();

        cout <<"the second line" <<endl;

    }


    int main()

    {

        thr = thread(func);

        thr.join();

        cout <<"main() function line" <<endl;


        return 0;

    }

The output is:

    the first line

    the second line

    main() function line

No error message was issued at runtime. The join() statement expected the thread to execute before the main() function body could continue. That happened despite the fact that the called thread was detached in the middle of its execution, with the statement,

        thr.detach();

And so the main() function (main thread) continued after the called thread had completed, with all its resources released by the implementation. In the second half of the called thread, the called thread was already detached, though the calling thread was still waiting.

The program begins with the inclusion of the iostream library for the cout object. Next, there is the inclusion of the thread library, which is a must. Then there is the instantiation of the thread, thr, without a function. The function it will use is defined just after. This function has the detached statement of the object, thr within its body.

In the main() function body, the first statement creates a thread of a function but without a name. This thread is then assigned to thr. So, thr now has a function, with the advantage that it was created in the global scope, so that it could be seen in func().

The next statement joins the function body of the main() function to the called thread. The thread was called in the first statement of the main() function. At this point, the main() function body waits for the called thread to run to its end and all its resources released, though it was detached in its middle. The join() function does its duty as long as anything inside the called thread is legitimate.

And so execution continues with the main function after the called thread has exited successfully, as anticipated (with all its resources released). That is why,

    “main() function line”

is outputted after all the outputs of the called thread.

Conclusion

Detaching a thread means the called thread can continue to execute, while the thread called it can also continue to execute. That is, the calling thread no longer continues to wait (block), after joining. This can increase the speed of both threads, enabling them to run in parallel and so increasing the speed of the whole program. In this case, it is best to detach the thread in its body, where communication between them will no longer occur. Also, to achieve this, let the thread variable be created in the global scope without its function. In the main() function of the C++ program, an anonymous thread, with the function of interest, can be created and assigned to the thread variable. This step calls the thread function, and so, calls the thread.

So, after the detach statement, the join() statement no longer has its normal role of waiting (blocking the calling thread), though it might still wait. It is not recommended to detach the called thread from the calling thread.

About the author

Chrysanthus Forcha

Discoverer of mathematics Integration from First Principles and related series. Master’s Degree in Technical Education, specializing in Electronics and Computer Software. BSc Electronics. I also have knowledge and experience at the Master’s level in Computing and Telecommunications. Out of 20,000 writers, I was the 37th best writer at devarticles.com. I have been working in these fields for more than 10 years.