3

If I spin off an std::thread in the constructor of Bar when does it stop running? Is it guaranteed to stop when the Bar instance gets destructed?

class Bar {

public:

Bar() : thread(&Bar:foo, this) {
}

...

void foo() {
  while (true) {//do stuff//}

}

private:
  std::thread thread;

};

EDIT: How do I correctly terminate the std::thread in the destructor?

user695652
  • 4,105
  • 7
  • 40
  • 58
  • 1
    See http://stackoverflow.com/a/13984169/12711 – Michael Burr Oct 19 '16 at 17:48
  • @kfsone thanks for the comment. Could you specify a bit more on which undefined behavior thread(&Bar::foo, this) is reliant on – user695652 Oct 19 '16 at 18:01
  • @user695652, it's generally dangerous to pass around `this` (or object members) in a constructor's initializer list. Your object only contains the thread so there's no harm in this example, but if it had more fields, you should be mindful that your thread could start executing before your object is fully constructed. – zneak Oct 20 '16 at 00:28

3 Answers3

2

If I spin off an std::thread in the constructor of Bar when does it stop running?

the thread will run as long as it executing the callable you provided it, or the program terminates.

Is it guaranteed to stop when the Bar instance gets destructed?

No. In order to guarantee that, call std::thread::join in Bar destructor.

Actually, if you hadn't call thread::join or thread::detach prior to Bar::~Bar, than your application will be terminated by calling automatically to std::terminate. so you must call either join (preferable) or detach (less recommended).

you also want to call therad::join on the object destructor because the spawned thread relies on the object to be alive, if the object is destructed while your thread is working on that object - you are using destructed object and you will have undefined behavior in your code.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • thank you for your answer. I don't understand the last part "if you hadn't call thread::join or thread::detach", so in my example above, what happens to the thread? – user695652 Oct 19 '16 at 17:50
  • if you don't call `thread::join` or `thread::detach` **and** the destructor of the thread starts to run - the entire application terminates. – David Haim Oct 19 '16 at 17:51
  • well, sort of. "crash" is when the operating system actually kills your process. here, the program itself exits. of course, the standard doesn't really defines the term "crash" – David Haim Oct 19 '16 at 17:55
  • I see, though I still don't understand how I would correctly terminate my thread in the destructor, yield woudl not work because my thread runs in an endless loop. – user695652 Oct 19 '16 at 17:57
  • @user695652: it's dangerous to terminate a thread from outside the thread (for example, it's not generally easy or possible for another thread to know whether the thread is holding a mutex) so the widely accepted way to terminate a thread is to somehow signal it to exit. Your thread's work loop could be something like `while (!exit_thread) ...` instead of `while (true) ...`. And `exit_thread` would be some object or function that can be used to tell the thread (in a thread-safe manner) that it should clean up and exit. – Michael Burr Oct 21 '16 at 17:08
0

Short answer: Yes and no. Yes, the thread ends, but not by the usual way (killing the thread), but by the main thread exiting due to a std::terminate call.

Long answer: The thread can only be safely destructed when the underlying function (thread) has finished executing. This can be done in 2 ways

  • calling join(), which waits for the thread to finish (in your case, never)
  • calling detach(), which detaches the thread from the main thread (in this case, the thread will end when the main thread closes - when the program terminates).

If the destructor is called if all of those conditions don't apply, then std::terminate is called:

  • it was default-constructed

  • it was moved from

  • join() has been called

  • detach() has been called

Community
  • 1
  • 1
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
0

The C++ threading facilities do not include a built-in mechanism for terminating a thread. Instead, you must decide for yourself: a) a mechanism to signal the thread that it should terminate, b) that you do not care about the thread being aborted mid-operation when the process terminates and the OS simply ceases to run it's threads any more.

The std::thread object is not the thread itself but an opaque object containing a descriptor/handle for the thread, so in theory it could be destroyed without affecting the thread, and there were arguments for and against automatic termination of the thread itself. Instead, as a compromise, it was made so that destroying a std::thread object while the thread remained running and attached would cause the application to terminate.

As a result, In it's destructor there is some code like this:

~thread() {
    if (this->joinable())
        std::terminate(...);
}

Here's an example of using a simple atomic variable and checking for it in the thread. For more complex cases you may need to consider a condition_variable or other more sophisticated signaling mechanism.

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

class S {
    std::atomic<bool> running_;
    std::thread thread_;
public:
    S() : running_(true), thread_([this] () { work(); }) {}
    void cancel() { running_ = false; }
    ~S() {
        if ( running_ )
            cancel();
        if ( thread_.joinable() )
            thread_.join();
    }
private:
    void work() {
        while ( running_ ) {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "tick ...\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "... tock\n";
        }
        std::cout << "!running\n";
    }
};

int main()
{
    std::cout << "main()\n";
    {
        S s;
        std::this_thread::sleep_for(std::chrono::milliseconds(2750));
        std::cout << "end of main, should see a tock and then end\n";
    }
    std::cout << "finished\n";
}

Live demo: http://coliru.stacked-crooked.com/a/3b179f0f9f8bc2e1

kfsone
  • 23,617
  • 2
  • 42
  • 74