4

I am trying to clean up gracefully on program termination, so I'm calling join() on a std::thread to wait for it to finish. This simply seems to block the main thread forever, but I don't understand why, because the worker thread is an (almost) empty loop like this:

void GameLoop::Run()
{
    while (run)
    {
        //  Do stuff... 
    }   
    std::cout << "Ending thread...\n";
}

I'm setting run to false before joining, of course. Now, I'm suspecting it's got something to do with it being a member function and being called upon object destruction. I'm creating the thread like this: runThread.reset(new thread(&GameLoop::Run, this));, where runThread is unique_ptr<std::thread> and a member of GameLoop. The join() call comes in the destructor of the GameLoop object.

Maybe the loop thread cannot finish if its object is in the process of being destroyed? According to the debugger the loop thread lingers on in the dark depths of msvcr120d.dll. If so, how would you handle it?

Beware: new to std::thread here!

Update: This is my call to join in the destructor:

run = false;
if (runThread->joinable())
{
    runThread->join();
}

Update 2: If I remove the join() I get an exception raised by ~thread()!

Kristian D'Amato
  • 3,996
  • 9
  • 45
  • 69
  • I hope you set run to false/0 somewhere... It is a good idea to define it as volatile. – Alex F Nov 13 '13 at 18:57
  • Do you ever set `run` to `false`? What joining a thread does is put your calling thread to sleep until the target thread is dead (with its various meanings), which is exactly what you're seeing. It doesn't kill the thread itself. – Blindy Nov 13 '13 at 18:58
  • Yes of course. The message is being printed and all. – Kristian D'Amato Nov 13 '13 at 18:58
  • While updating your question, also mention what `thread::joinable` returns, if the thread isn't joinable you obviously shouldn't join on it. What happens if you remove the join? If the program closes fine (check task manager) it means the thread was successfully cleared by the compiler. – Blindy Nov 13 '13 at 19:03
  • 2
    Can you post the relevant code for your destructor? – Zac Howland Nov 13 '13 at 19:04
  • 2
    @AlexFarber - it's an even better idea to define it as `std::atomic`. `volatile` has no portable semantics, and isn't useful for inter-thread synchronization. – Pete Becker Nov 13 '13 at 19:07
  • 1
    If you remove the join(), you get an exception because the thread object cannot be destructed until the thread finishes (except if detach has been called on it). – Mark Vincze Nov 13 '13 at 19:18
  • 1
    I have no trouble with what you describe your code does...http://coliru.stacked-crooked.com/a/7555dac44353ca50 – melak47 Nov 13 '13 at 19:19
  • Hmm, thanks, I don't see why it's happening here. Must be something to do with destruction, I think, because if I call the join code before the program doesn't freeze. – Kristian D'Amato Nov 13 '13 at 19:26
  • Related: https://stackoverflow.com/questions/9094422/how-to-check-if-a-stdthread-is-still-running – Rufus Oct 13 '21 at 07:34

2 Answers2

5

Of course, when you join a thread that doesn't cause the thread to terminate. It simply blocks until that thread dies of natural (or unnatural) causes.

In order to clean up a multithreaded application gracefully, you need to somehow tell the worker thread that it is time to die, and then wait for the death to happen. The "wait for death to happen" part is what join is for.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
2

Ahh, apparently there's a bug in the runtime library. Threads are not ended successfully in destructors of static objects according to this question. My GameLoop is a non-static object contained in a static GameSystem. I'll check whether this is true and update.

Yep, confirmed. Just my luck to hit a bug on first use!

Community
  • 1
  • 1
Kristian D'Amato
  • 3,996
  • 9
  • 45
  • 69
  • Well you'll also want to ensure that `run` functions properly in a multi-threaded environment. – Lightness Races in Orbit Nov 14 '13 at 08:27
  • Ok, thanks, I've changed it to an atomic, but I wonder: for such a small variable like a bool, is there a real concern to make it an atomic, if one thread is only writing and another is only reading? – Kristian D'Amato Nov 14 '13 at 09:57
  • @KristianD'Amato No, if you're comfortable trusting the compiler when the behavior of multiple threads accessing a non-atomic variable is explicitly undefined behavior in C++11. – JAB Nov 03 '15 at 20:24