0

I'm new to C++ and I'm trying to make the console print "after 5 seconds" after 5000 ms. Then print "insta log" immediately after the new thread's declaration.

But doing so crashes with the following error:

"Debug Error!

[PROGRAM PATH]

abort() has been called

"

This is my code:

#include <iostream>
#include <thread>
#include <Windows.h>
#include <ctime>

using namespace std;

void f() {
    Sleep(5000);
    cout << "after 5 seconds" << endl;
}

int main() {
    cout << "starting" << endl;
    

    // Pass f and its parameters to thread 
    // object constructor as
    thread t(&f);
    cout << "insta log" << endl;

}

I'm unsure why this is happening. I've searched around and I found a "fix" but it makes my code not behave as intended.

This is the "fix"

#include <iostream>
#include <thread>
#include <Windows.h>
#include <ctime>

using namespace std;

void f() {
    Sleep(5000);
    cout << "after 5 seconds" << endl;
}

int main() {
    cout << "starting" << endl;
    

    // Pass f and its parameters to thread 
    // object constructor as
    thread t(&f);

    t.join();

    cout << "insta log" << endl;    // doesn't print for 5 seconds

}

This removes the error message but yields the main thread for 5 seconds. Which makes my code not work as intended.

Thanks in advance, any help is appreciated!

BIack1st
  • 1
  • 1
  • 3
  • 4
    ... you could `join()` after you print in `main()`. – Yksisarvinen Mar 29 '22 at 00:39
  • And yes, exiting main thread while there are still other threads running is Undefined Behaviour, you must `join()` each thread or make sure that they ended in some other way before `main` ends. Related/duplicate: [Why must one call join() or detach() before thread destruction?](https://stackoverflow.com/questions/57066847/why-must-one-call-join-or-detach-before-thread-destruction) – Yksisarvinen Mar 29 '22 at 00:39
  • 1
    Something else that might help: https://stackoverflow.com/questions/37015775/what-is-different-between-join-and-detach-for-multi-threading-in-c – Jerry Jeremiah Mar 29 '22 at 00:42
  • [Please do not upload images of code/errors when asking a question.](//meta.stackoverflow.com/q/285551) – Ken White Mar 29 '22 at 00:43
  • didn't know that. Thanks for the info, Is there a way I can edit posts? – BIack1st Mar 29 '22 at 00:45
  • @Yksisarvinen this worked thank you! Didn't know that you could do that. – BIack1st Mar 29 '22 at 00:46
  • Underneath the question you'll find a bunch of links. One will be [Edit](https://stackoverflow.com/posts/71655186/edit). – user4581301 Mar 29 '22 at 00:47
  • @user4581301 thanks, I've gone ahead and edited the post. Thanks for your help everyone! – BIack1st Mar 29 '22 at 00:49

3 Answers3

1

This removes the error message.

Yes.

but yields the main thread for 5 seconds.

This is not what is happening! The main thread is waiting for your second thread t to finish (slightly different to yielding).


The problem before was that the main thread was exiting the application (and you are not allowed to have other threads running after the main thread exits (this is because what happens to the children thread is highly depended on the threading implementation and they very wildly).

In the C++ std::thread class they try and compensate for the above behavior by making the destructor terminate() if the current thread leaves scope without the child thread of execution completing.

This means you usually have to call the join() method to wait for the child to exit.

So what you usually do is 1: create a std::thread object that does some work in the background 2: while you do some work locally. Then when you have finished, 3: you call join() and wait for the child object to also finish (if it has already finished this does nothing). Then you can exit scope (and exit the main).

Which makes my code not work as intended.

int main()
{
    // STUFF.


    // Create your thread.
    thread t(&f);

    // Print any thing you want.
    // i.e. do the work you want to do in main.
    cout << "insta log" << endl;


    // When you have finished.
    // wait for the child to finish.
    t.join();

} // now the std::thread::~thread check to make sure the
  // child thread of execution is no longer running.
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

If you had clicked 'retry' in that dialog you would have seen why your code died. Its right here

~thread() noexcept {
    if (joinable()) {
        _STD terminate();
    }
}

in the MSVC implementation of std::thread. The code says that destroying a thread thats still joinable (ie is running) is illegal. I dont know if thats c++ defined behavior, a quick search didnt show me. Anyway when you join the thread, you will wait till its OK to destroy it

pm100
  • 48,078
  • 23
  • 82
  • 145
-1

Functions are already passed around as pointers, use thread t(f) instead of thread t(&f).

Moreover, since your main() neither lasts longer than the thread or calls a t.join(), the program will end before the thread finishes it's code, so that might be another reason for a crash. In fact it is probably the reason for the crash.

If you want "insta log" to print instantly, then call t.join() at the end of main(). t.join() will wait for the thread t to end before continuing.

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • 1
    `std::thread(f)` and `std::thread(&f)` are equivalent: https://stackoverflow.com/questions/34289722/function-name-and-pointer-to-function – Yksisarvinen Mar 29 '22 at 01:00
  • Yes, but that's one of those weird things in C, he is doing an explicit referencing but such referencing yields the same original value, because you're not supposed to reference a function. It should be an error, but the compiler shrugs it off. Besides, it's helpful to understand that a function is already a pointer. – Havenard Mar 29 '22 at 01:06
  • It is the thread destructor that is terminating the application (because it's associated thread of execution is still running). – Martin York Mar 29 '22 at 01:16
  • 2
    @Havenard A function is not a pointer. If you did `decltype(f)` you would get `void()`, not a pointer-to-function type, in contrast to `decltype(&f)`. However in an expression an lvalue to a function is implicitly converted to a function pointer prvalue where a prvalue is required. `&f` is defined to have the same effect as that conversion. `&f` is exactly as correct as `f` here and every standard-compliant compiler needs to accept both. – user17732522 Mar 29 '22 at 01:17
  • It's not helpful, because `f` is not a pointer. It can only be implicitly converted to pointer. Godbolt example: https://godbolt.org/z/169W85PdT Relevant C++ standard fragment: https://eel.is/c++draft/conv.func#:conversion,function-to-pointer – Yksisarvinen Mar 29 '22 at 01:18
  • Well yes, I mean, it couldn't possibly be passed as anything else, so it's implicitly a pointer and the conversion is just silly. I believe it is helpful because reminds the programmer that in the machine code underneath, functions are nothing but memory addresses the thread jump to. – Havenard Mar 29 '22 at 01:26
  • 1
    Would you argue the same about arrays, because they decay to pointers when passed to function? Would you argue the same about converting `int` to `double`, which can be done implicitly? For C++ programmer types are significantly more important than knowing what happens in machine code. And I agree that distinction between function type and pointer to function is rarely useful, but you can still trip over that in template metaprogramming. And what about member functions? They are not implicitly pointers. – Yksisarvinen Mar 29 '22 at 01:45
  • @Yksisarvinen No I wouldn't, because unlike functions, those things are actual variables. Besides getting their address, you can perform a bunch of operations, both with their pointers and with themselves, but not with functions, those are completely different. A function is handled around as a memory address. Maybe I'm being too generic when I call them pointers, obviously they're not full blown pointers in stricto sensu, what I mean is that they are memory addresses for all intents and purposes, and the language will implicitly handle them as pointers. – Havenard Mar 29 '22 at 05:45