【问题标题】:Is leaking std::thread undefined behaviour?泄漏 std::thread 未定义行为吗?
【发布时间】:2011-11-16 10:07:27
【问题描述】:

有人会感兴趣的原因

  //...
 new std::thread (func,arg1,arg2);

}

std::thread析构函数(不像boost::thread)杀死线程。 func 完成需要一些时间。 我的问题是这种情况下是否安全:

案例 1:假设函数按值获取 arg1、arg2。
案例 2:假设函数通过引用获取 arg1、arg2 - 如果您问我,这听起来很糟糕,因为我假设在创建线程的作用域结束时,arg1 和 arg2 将被它们的析构函数清除。

顺便说一句,std::thread 析构函数(即 func 完成时调用的 AFAIK)足够聪明,可以清除线程使用的所有资源?我的意思是,如果我用新的线程创建 1M 线程(不是同时)并且所有线程都完成,我是否永久泄漏了任何东西?

【问题讨论】:

  • 您没有在那里泄漏线程,在该块结束后您仍然有一个指向它的指针。所以我认为你的问题标题有点误导,并没有真正反映你的问题。

标签: c++ multithreading c++11


【解决方案1】:

根据最终文本,这是好的。具体来说,既不是joined 也不是detached 的线程的析构函数将调用std::terminate

std::thread 析构函数不会在回调返回时调用,而是在thread 对象超出范围时调用,就像任何其他对象一样。所以,一个永远不会被delete'd 的new'd 线程永远不会被破坏,泄漏任何相关的状态(如果/当程序终止而任何额外的线程仍在运行时,事情可能会发生可怕的错误......)

为了回答您关于生命周期的问题,std::thread 类似于std::bind:它存储其参数的副本并将这些副本传递给回调,无论回调是通过值还是通过引用获取其参数。您必须使用 std::ref 显式包装参数以获得引用语义。在这种情况下,像往常一样,您需要确保不会出现悬空引用(例如,在任何引用的变量超出范围之前调用 thread::join()。)

【讨论】:

  • 但是没有人删除这里分配的指针,那么析构函数首先将如何运行?
【解决方案2】:

顺便说一句,std::thread 析构函数(即 func 完成时调用的 AFAIK)足够聪明,可以清除线程使用的所有资源?我的意思是,如果我用 new 创建 1M 线程(不是同时)并且所有线程都完成,我是否会永久泄漏任何东西?

通常,当堆栈上的对象超出范围时(或在堆栈展开期间,当捕获到异常时)会自动调用析构函数。由于线程对象是使用new在堆上分配的,因此不会自动调用析构函数,因此某些代码需要delete对象来调用其析构函数。换句话说,thread 的析构函数不会在 func() 完成时被调用。

【讨论】:

    【解决方案3】:

    一个 std::thread 对象代表一个线程,它不是一个“真正的”线程,这是一个由操作系统管理的概念。

    因此,这里你泄漏了一个 std::thread 对象,因为你没有删除它。 永远不会调用析构函数。 但是它所代表的线程将在它的指令结束时结束,就像任何线程一样。

    If the destructor was called before the end of the instructions, for example if you would have created that object on the stack and there was a lot of things to do in this new thread, then the destructor call would have triggered std::terminate(). You need to call detach() to allow the thread to continue after the std::thread instance is destroyed.

    那样,您必须明确说明是否要在 std::thread 实例被销毁时结束此线程:如果您希望它继续,请调用 detach();如果您想等待它结束,请调用 join()。

    因此,在您的情况下,您会泄漏内存,但线程应该继续,因为 std::thread 对象“永远”存在,因为您失去了控制它的任何方法。

    【讨论】:

    • 不错的答案。有什么办法可以做我想做的事。在不加入的情况下基本安全地在新线程中运行函数?
    • 安全地我的意思是它清理它的范围(调用析构函数)并清理自己。
    • @NoSense:您必须在该线程中构建一个系统才能在其中“运行函数”。请记住:线程是一个简单的低级构造。它执行您告诉它的功能,并在到达该功能的末尾时结束。如果你想做你所说的,你需要让线程函数寻找要执行的命令,等到你从另一个线程提供它们,然后执行它们。当它完成这些命令时,它会等到你给它更多。所有这些逻辑都必须由您编写(或者您使用库)。
    • 如果函数是:try{//work} catch(...){//什么都不做,除了 completed=false},会不会让它运行并忘记(ofc 你需要如果要使用 func 修改的数据,请检查已完成 var 的值。)。
    • @NoSenseEtAl 您可以只使用detach() 线程(它将自行运行),或者您可以使用std::async,避免std::thread 这是一个挑剔的低级构造将 std::terminate 在最轻微的挑衅。
    猜你喜欢
    • 2018-05-10
    • 2014-07-30
    • 1970-01-01
    • 2019-11-17
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    相关资源
    最近更新 更多