7

Learning C++ multi-threading.
In my example, thread helper1 and helper2 have finished executing before the main thread finished. However, program crashes. I specifically, took out .join() statements, to see how program would behave, expecting no errors, since main() calls std::terminate after two other threads have finished.

void foo()
{
    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "t1\n";
}

void bar()
{
    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "t2\n";
}

int main()
{

    std::cout << "starting first helper...\n";
    std::thread helper1(foo);

    std::cout << "starting second helper...\n";
    std::thread helper2(bar);


    std::this_thread::sleep_for(std::chrono::seconds(10));

    std::cout << "waiting for helpers to finish..." << std::endl;
    //helper1.join();
    //helper2.join();

    std::cout << "done!\n";
}
newprint
  • 6,936
  • 13
  • 67
  • 109

4 Answers4

8

Because std::~thread calls terminate if the associated thread is still joinable:

30.3.1.3 thread destructor [thread.thread.destr]

~thread();

If joinable(), calls std::terminate(). Otherwise, has no effects. [ Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. —end note]

You need to call either .detach() or .join(). Other than that, since you cannot be sure how the operating system schedules your threads, you could end up interrupting your threads any way, so better use .join() from the beginning.

Zeta
  • 103,620
  • 13
  • 194
  • 236
8

I'd say that your question doesn't make sense, because it's based on a false assumption. The only way to know that a thread has finished is when the thread's join() returns. Before join() returns, it is not the case that "the thread has finished". It may be true that some statement within the thread's execution has completed (e.g. the printing of a message, or better, the writing of an atomic variable), but the completion of the thread function itself is not measurable in any way other than by joining.

So none of the threads "have finished" until you join them.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I would add to the question: "What happens if main thread ends and there are still running threads at OS level"? So what if std::thread::~thread() don't call "terminate"? – CoffeDeveloper Nov 08 '14 at 13:46
  • 1
    @DarioOO: You can achieve that simply by calling `detach` on the thread. Then there's no terminate. – Kerrek SB Nov 08 '14 at 14:07
4

Based on the reference, underlying thread must be joined or detached at the time the destructor is called. The destructor is invoked when main exits, and probably assumes that join or detach has been called.

The code should also not crash, as long as the following two lines are somewhere after helper1 and helper2 are constructed.

helper1.detach()
helper2.detach()
merlin2011
  • 71,677
  • 44
  • 195
  • 329
3

The CPU can schedule the three threads ( main / thread1 / thread2 ) in any order. It might happen that your main doesn't get a time to run and your threads exit. So, you need to keep keep join in main to take care of this case. Scheduling of threads is unpredictable, unless you are using an RTOS.

brokenfoot
  • 11,083
  • 10
  • 59
  • 80