使用参数创建时,std::thread 抛出访问冲突异常? [关闭]

Posted

技术标签:

【中文标题】使用参数创建时,std::thread 抛出访问冲突异常? [关闭]【英文标题】:std::thread throws Access violation exception when created with arguments? [closed] 【发布时间】:2016-08-24 13:34:46 【问题描述】:

我用的是VS2015,在使用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();

它在 Debug 模式下运行良好,但在我切换到 Release 模式时会引发“访问冲突异常”。

此外,如果我尝试将“myfunc”修改为:

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

它再次运行良好。

我保证 "&Klass::myfunc" 和 "this" 指针不为 NULL。调用ctor时,在几行之后有一个“join”。

我猜这可能是某种“未定义的行为”,但我不知道它到底是什么。

调用栈是这样的:

    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

【问题讨论】:

创建线程后会发生什么?你join了吗? @doctorlove 可能暗示的是,这看起来像是一个生命周期问题,其中线程比 Klass 实例寿命更长,因此有一个悬空的 this 指针。通过在正确的位置加入,您可以防止这种情况发生。但是,根据所提供的背景,我们无法确定。 @doctorlove stefaanv 两位您好,感谢您的回复。实际上,调试器和日志显示程序在调用 std::thread 的 ctor 后立即关闭,并且“join”在几行之后。我认为问题不在于“加入”。而且我在问题中也提到,如果我不带参数调用“myfunc”,一切都会顺利。 请编辑您的问题以包含minimal reproducible example 【参考方案1】:

您应该始终确保加入(或可能分离)thread,否则留下 main 尤其是使用对象的线程(在本例中为 this)会(有时)导致问题。

//... details omitted...

int main()

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

Anthony William 的线程blog 详细介绍了这一点。举一个与你的第二个非常相似的例子:

void my_thread_func()

    std::cout<<"hello"<<std::endl;


int main()

    std::thread t(my_thread_func);

他说

如果您编译并运行这个小应用程序,会发生什么?可以 像我们想要的那样打印你好?嗯,其实也没什么好说的。它 可能会,也可能不会。我多次运行这个简单的应用程序 在我的机器上,输出不可靠:有时它输出 “你好”,带有换行符;有时它会输出“hello”而没有 换行,有时它没有输出任何东西。那是怎么回事? 像这样一个简单的应用程序肯定应该可以预测吗?

然后他介绍了使用join 的想法,就像我在上面所做的那样,并说,

问题是我们没有等待我们的线程完成。当。。。的时候 执行到 main() 程序结束,程序终止, 无论其他线程在做什么。

【讨论】:

以上是关于使用参数创建时,std::thread 抛出访问冲突异常? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

为啥构造 std::thread 时参数移动了两次

std::thread创建线程,使用std::ref()传递类对象参数

std::thread创建线程,使用std::ref()传递类对象参数

std::thread创建线程,使用std::ref()传递类对象参数

如何在 freeRTOS 上使用 std::thread?

通过 std::thread 将参数传递给函数时出现语法错误