A new thread in C++20 (jthread)

Vanand Gasparyan
Aug 27 · 5 min read

C++20 is coming with a bunch of cool new features, one of which I’ll shortly cover here: the std::jthread.

The implementation of this std::jthread is based on the already existing std::thread. It’s basically a wrapper that brings two new features to the threads: they are cooperatively interruptible and join by default. Before going deeper into these two terms, note that the std::jthread object wraps anstd::thread as a member, providing the very same public functions, which simply transfer down the calls. This enables us to change any std::thread into std::jthread, knowing for sure that it’ll work as before.

The jthread is cooperatively interruptible

The name suggests that the new jthread is interruptable, i.e. there is a way to stop a thread from outside. Unlike C++, in some other languages the thread classes have abort(), stop() or interrupt() functions and mostly they are not what users might expect, that is, a kill switch. Some might think it’s so bad that we don’t have such a thing with std::thread and that it’s so good that now with std::jthread we finally have it. But it’s cooperatively interruptable, and the best way to understand this is to take a look at the function for it: request_stop(). The name is chosen very carefully. Consider this example:

Here the main thread creates a new thread, which repeatedly does something (prints a line) every second, forever. The main thread then continues with a 5-second-job, after which waits for the other thread to finish. But it won’t finish, it will keep on running forever, and the main thread will keep on waiting.

We’ve just changed the thread to jthread and, as promised, nothing new happens, it behaves as before. Now let’s use that function to stop the thread after main is done.

This compiles and is valid, but it doesn’t stop the execution of that thread, not instantly, not ever. And that’s ok: note, it says request stop, not insist or force. So we (from outside the thread) can only request a stop and that thread itself has the final say. That’s why it’s cooperatively interruptable.

This finally works. Of course, we could have achieved this with an atomic boolean, and this is a more extensive and trustworthy version of it. But the question still remains: why don’t we have a killswitch on jthread, especially when others (other languages) do? The answer is: others don’t really have it and you don’t want to have it either.

Why a killswitch is a bad idea?

Say you’ve created a worker thread from the main thread and at some point, you want to kill it (the worker). Imagine there is a killswitch, a stop() function which:

  • removes worker from the thread scheduler (PAUSE)
  • frees the memory worker used as a stack, without calling the destructors (WIPE OUT)

This almost always guarantees a deadlock or a memory leak or both. So this definitely is a bad idea, some cleanup is necessary anyway. What other languages mostly do is throw an exception from worker, which lays the responsibility of catching and doing the cleanup back on your shoulders. Let’s not forget that std::thread is just a cover, and mostly it’s a pthread underneath. It also has somewhat similar and we can always get the native_handle() from the std::thread and work with it. But it’s even more complicated.

Yes, it’s complicated. Don’t bother reading.

But the idea is the same: before using that “killswitch” you should take care of the cleanup yourself.

Now, imagine you’re inventing a new language that supports multithreading and has a Thread class. Would you give it a stop functionality which isn’t really what the name suggests and contains a high risk of misuse? I would not, so that users are forced to find a way around, the right way.

The jthread joins by default

The second feature jthread brings is to help us with the dilemma std::thread used to lead to: to join or to detach. Now jthread comes to take some responsibility. Its destructor is simply implemented as:

This is exactly what we’ve done in our example above, and so we can remove the last two lines there.

But why the join was chosen as the default end for a jthread? First of all, it’s very safe compared to the detach, and also, in most cases, that’s what you really need.

To get a better understanding of the dangers of detach and why joining should always be your choice check this out: “Let me detach those threads for you”.

Even though it comes with C++20, std::jthread doesn’t use any of the new language features, so it’s practically available even now. Here is an implementation of it by Nicolai Josuttis, the person who proposed it. Two header files from this repository are all you need to fully access this new feature: “jthread.hpp” and “stop_token.hpp” in “sources/”.

    Vanand Gasparyan

    Written by

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade