4

I'm using VS2015 and encounter a extremely strange problem when using std::thread.

void Klass::myfunc(int a, int b) { std::cout << a << ' ' << b << std::endl; }
// ...
auto t = std::thread(&Klass::myfunc, this, 100, 200); <- runtime error after called
// ...
t.join();

It works well on Debug mode, but throws an "Access violation exception" when I turn to Release mode.

What's more, if I try to modify "myfunc" to this:

void Klass::myfunc() { std::cout << "foo" << std::endl; }
// ...
auto t = std::thread(&Klass::myfunc, this); // everything goes well
// ...
t.join();

it works well again.

I'm guarantee that "&Klass::myfunc" and "this" pointers are not NULL. And there is a "join" after several lines when the ctor is called.

I guess it might be some kind of "undefined behavior" but I have no idea what is it exactly.

The call stack is something like this:

    000000c83a4ffd40()  Unknown
>   distributed_word_embedding.exe!std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl multiverso::Communicator::*)(void) __ptr64,multiverso::Communicator * __ptr64>,std::default_delete<std::tuple<void (__cdecl multiverso::Communicator::*)(void) __ptr64,multiverso::Communicator * __ptr64> > > >::_Run(std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl multiverso::Communicator::*)(void),multiverso::Communicator *>,std::default_delete<std::tuple<void (__cdecl multiverso::Communicator::*)(void),multiverso::Communicator *> > > > * _Ln) Line 247    C++
    distributed_word_embedding.exe!std::_Pad::_Call_func(void * _Data) Line 210 C++
    ucrtbase.dll!00007ffabdc7be1d() Unknown
    kernel32.dll!00007ffabfae8102() Unknown
    ntdll.dll!00007ffac26bc5b4()    Unknown
Wizmann
  • 839
  • 1
  • 9
  • 14
  • 6
    What happens after you make the thread? Do you `join` it? – doctorlove Aug 24 '16 at 13:37
  • 3
    What @doctorlove probably is hinting at is that this looks like a lifetime issue where where the thread outlives the Klass instance and thus has a dangling this-pointer. By joining at the right place, you can prevent this. However we can't be sure based on the presented contex. – stefaanv Aug 24 '16 at 13:45
  • @doctorlove stefaanv hi both, thanks for your reply. Actually the debugger and log shows that the program is down right after the ctor of std::thread is called, and the "join" are several lines after. I think the problem is not about "join". And I also mentioned in the question that if I call "myfunc" without arguments, everything goes well. – Wizmann Aug 24 '16 at 15:39
  • Please edit your question to contain a [mcve] – Slava Aug 24 '16 at 20:30

1 Answers1

0

You should always make sure you join (or possibly detach) a thread, otherwise leaving main especially with a thread using objects (in this case this) will (sometimes) cause problems.

//... details omitted...

int main()
{
  auto t = std::thread(&Klass::myfunc, this);
  t.join();  //<----- NOTE THIS
}

Anthony William's threading blog goes through this in detail. With an example very similar to your second one:

void my_thread_func()
{
    std::cout<<"hello"<<std::endl;
}

int main()
{
    std::thread t(my_thread_func);
}

He says

If you compile and run this little application, what happens? Does it print hello like we wanted? Well, actually there's no telling. It might do or it might not. I ran this simple application several times on my machine, and the output was unreliable: sometimes it output "hello", with a newline; sometimes it output "hello" without a newline, and sometimes it didn't output anything. What's up with that? Surely a simple app like this ought to behave predictably?

He then introduces the idea of using join as I did above, and says,

The problem is we're not waiting for our thread to finish. When the execution reaches the end of main() the program is terminated, whatever the other threads are doing.

doctorlove
  • 18,872
  • 2
  • 46
  • 62