3

So I ran across something that seems to defeat the purpose of std::thread or at least makes it less convenient.

Say I want to spawn an std::thread to perform a task one time and don't want to worry about it again after that. I create this thread near the end of a function so the std::thread will soon go out of scope, in my case, likely while the thread is still running. This creates a problem with a couple of solutions (or at least that I know of).

I can:

A) Make the std::thread a global variable so it doesn't go out of scope.

B) Call join() on the std::thread which defeats the purpose of spawning the thread.

Are there other, hopefully better, ways to handle this kind of situation?

  • If you don't care about the task, why launch it in the first place? – n. m. could be an AI Sep 06 '18 at 05:04
  • What's the problem with the `std::thread` object being destroyed? It's as much the thread as a `std::fstream` is the file, they are both just handles to a resource, not the resource itself. Also, why does it matter that it's a `std::thread` and how would you do it with a different kind of object, say `std::string`? – Ulrich Eckhardt Sep 06 '18 at 05:07
  • @Ulrich read the [fine manual](https://en.cppreference.com/w/cpp/thread/thread/~thread). – n. m. could be an AI Sep 06 '18 at 05:14
  • I'm aware of possible problems @n.m., but the description is simply lacking and that's important to pick the right approach. – Ulrich Eckhardt Sep 06 '18 at 05:16
  • @UlrichEckhardt From what I understand calling the destructor of an `std::thread` before calling `join()` or `detach()` causes `terminate()` to be called on the thread which ends it forcefully. An `std::string` isn't exactly comparable to a thread because the thread is running a separate task. A string is a piece of data. I'm not quite sure what you're getting at. – Edward Severinsen Sep 06 '18 at 05:41
  • @n.m. The task simply iterates through an array of classes and assigns one member variable in each to a specific value. However, the array is large enough that this task takes some time which is why I use a separate thread. This is a pretty simple task that doesn't really entail any exceptions or a meaningful return value which is why I just want to spawn the thread and leave it be. Not so much that I don't care, just that there's nothing more needed than starting the thread. – Edward Severinsen Sep 06 '18 at 05:48
  • 1
    The problem here is that you never know when the task finishes, or whether it finishes at all. So your program may terminate even before your thread starts doing its work. This approach is mostly good for tasks that are optional. In addition, if your task modifies global data without any synchronisation, you open yourself to all kinds of nasty consequences. – n. m. could be an AI Sep 06 '18 at 06:02
  • This seems more like a lack in understanding of the language then anything specific to std::thread. There are a lot of options other then local variable in the function and a global. A simple thread pool or task system need not be more then 20 lines of code and can be reused in the future. – super Sep 06 '18 at 06:27
  • @super I can't know what I don't know, ya know? You are correct, it is purely out of ignorance of certain features and methods which I addressed: `This creates a problem with a couple of solutions (or at least that I know of).` I haven't done much with multi-threading except some projects that were done in free time and for fun. I've never done C++ in what I would call a "professional environment". – Edward Severinsen Sep 06 '18 at 06:43
  • 1
    @EdwardSeverinsen Ofcourse. I'm only trying to say that I think it's better to leave the multithreading aspect out of how you manage object lifetime. All you are looking for is somewhere to store your thread object. It could be any object, and managing it's lifetime is something you deal with even if threads are not involved. Break problems down to smaller parts. – super Sep 06 '18 at 06:49
  • 1
    Destroying the `std::thread` "causes terminate() to be called on the thread" is not the case, it "causes terminate() to be called", terminating your process. – Ulrich Eckhardt Sep 06 '18 at 14:41
  • @UlrichEckhardt Noted. Thank you for clearing up my misconception. – Edward Severinsen Sep 07 '18 at 02:08

3 Answers3

5

What you want is std::thread::detach.

It decouples the actual thread of execution and the std::thread object, allowing you to destroy it without joining the threads.

Ap31
  • 3,244
  • 1
  • 18
  • 25
  • You have to be careful with detached threads. How do you handle exceptions? How do know/control if the thread is running or has finished? – AchimGuetlein Sep 06 '18 at 05:39
  • @AchimGuetlein `std::thread`s are terminated if the exception is thrown, regardless of whether they are detached or not. To communicate with the thread you have all the usual mechanics - `std::future`, `std::mutex`, atomics etc - again, they don't depend on the `std::thread` object – Ap31 Sep 06 '18 at 07:47
  • 1
    you are right. There are ways to handle all these problems. My point was that if you don't address these problems, you might get into troubles. And if you do, it might be simpler to use the `std::future` returned by `std::async` – AchimGuetlein Sep 06 '18 at 13:45
2

You can use std::async.

async runs the function f asynchronously (potentially in a separate thread which may be part of a thread pool) and returns a std::future that will eventually hold the result of that function call.

Since you did not mention that you need to get the result of the thread, there is no need to get the future.

As pointed out in the comments, the destructor of std::future will block in any case, so you will have to move it to some global object (and manually manage deletion of unused futures), or you can move it into the local scope of the asynchronously called function.

P.W
  • 26,289
  • 6
  • 39
  • 76
1

Other preferred option is to allow the thread to consume tasks from a task-queue. For that, split the job into task chuncks and feed to the worker thread. To avoid polling, opt for condition_variable.

std::thread([&](){
                    while(running) // thread loop
                    { 
                       // consume tasks from queue
                    }
                 }).detach();
seccpur
  • 4,996
  • 2
  • 13
  • 21