std::thread 线程在对象中分离,它何时终止?

Posted

技术标签:

【中文标题】std::thread 线程在对象中分离,它何时终止?【英文标题】:std::thread thread spun off in object, when does it terminate? 【发布时间】:2016-10-19 17:43:59 【问题描述】:

如果我在Bar 的构造函数中拆分std::thread,它什么时候停止运行?当Bar 实例被破坏时,它是否保证停止?

class Bar 

public:

Bar() : thread(&Bar:foo, this) 


...

void foo() 
  while (true) //do stuff//



private:
  std::thread thread;

;

编辑:如何正确终止析构函数中的std::thread

【问题讨论】:

见***.com/a/13984169/12711 @kfsone 感谢您的评论。您能否详细说明一下哪个未定义的行为线程(&Bar::foo, this)依赖于 @user695652,在构造函数的初始化列表中传递this(或对象成员)通常很危险。您的对象仅包含线程,因此在此示例中没有任何危害,但如果它有更多字段,您应该注意您的线程可能在对象完全构造之前开始执行。 【参考方案1】:

如果我在 Bar 的构造函数中剥离一个 std::thread 什么时候做 停止运行?

只要线程执行您提供的可调用对象,线程就会运行,或者程序终止。

Bar 实例被破坏时是否保证停止?

没有。为了保证这一点,在Bar析构函数中调用std::thread::join

实际上,如果您在Bar::~Bar 之前没有调用thread::jointhread::detach,那么您的应用程序将通过自动调用std::terminate 来终止。所以您必须致电join(首选)或detach(不太推荐)。

您还想在对象析构函数上调用therad::join,因为如果在您的线程正在处理该对象时对象被破坏,则生成的线程依赖于对象是活动的 - 您是使用破坏的对象,您的代码中将有未定义的行为。

【讨论】:

感谢您的回答。我不明白最后一部分“如果你没有调用 thread::join 或 thread::detach”,那么在我上面的例子中,线程会发生什么? 如果你不调用thread::jointhread::detach 并且线程的析构函数开始运行——整个应用程序终止。 嗯,有点。 “崩溃”是操作系统实际杀死您的进程的时间。在这里,程序本身退出。当然,标准并没有真正定义术语“崩溃” 我明白了,虽然我仍然不明白如何在析构函数中正确终止我的线程,但由于我的线程在无限循环中运行,yield 不会起作用。 @user695652:从线程外部终止线程是危险的(例如,另一个线程通常不容易或不可能知道线程是否持有互斥锁),因此被广泛接受的方式终止线程是以某种方式发出信号退出。您的线程的工作循环可能类似于while (!exit_thread) ... 而不是while (true) ...exit_thread 将是一些对象或函数,可用于告诉线程(以线程安全的方式)它应该清理并退出。【参考方案2】:

简短回答:是和否。是的,线程结束,但不是通过通常的方式(杀死线程),而是通过主线程由于std::terminate 调用而退出。

长答案:只有在底层函数 (thread) 执行完毕后才能安全地销毁线程。这可以通过两种方式完成

调用join(),等待线程完成(在你的情况下,永远不会) 调用detach(),将线程与主线程分离(在这种情况下,线程将在主线程关闭时结束 - 当程序终止时)。

如果在所有those conditions 都不适用的情况下调用析构函数,则调用std::terminate

它是默认构造的

它被移出

join() 已被调用

detach() 已被调用

【讨论】:

【参考方案3】:

C++ 线程工具包含用于终止线程的内置机制。相反,您必须自己决定:a)一种机制来通知线程它应该终止,b)当进程终止并且操作系统只是停止运行它的线程时,您不关心线程在操作中被中止更多。

std::thread 对象不是线程本身,而是一个包含线程描述符/句柄的不透明对象,因此理论上它可以在不影响线程的情况下被销毁,并且有支持和反对自动终止线程的论据本身。相反,作为一种折衷方案,它是为了在线程保持运行和连接状态时销毁 std::thread 对象会导致应用程序终止。

因此,在它的析构函数中有这样的代码:

~thread() 
    if (this->joinable())
        std::terminate(...);

这是一个使用简单原子变量并在线程中检查它的示例。对于更复杂的情况,您可能需要考虑使用condition_variable 或其他更复杂的信号机制。

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

class S 
    std::atomic<bool> running_;
    std::thread thread_;
public:
    S() : running_(true), thread_([this] ()  work(); ) 
    void cancel()  running_ = false; 
    ~S() 
        if ( running_ )
            cancel();
        if ( thread_.joinable() )
            thread_.join();
    
private:
    void work() 
        while ( running_ ) 
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "tick ...\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "... tock\n";
        
        std::cout << "!running\n";
    
;

int main()

    std::cout << "main()\n";
    
        S s;
        std::this_thread::sleep_for(std::chrono::milliseconds(2750));
        std::cout << "end of main, should see a tock and then end\n";
    
    std::cout << "finished\n";

现场演示:http://coliru.stacked-crooked.com/a/3b179f0f9f8bc2e1

【讨论】:

以上是关于std::thread 线程在对象中分离,它何时终止?的主要内容,如果未能解决你的问题,请参考以下文章

从线程中分离对象

何时在 tableView 单元中分离 firebase 侦听器?

分离的 std::thread 终止后是不是需要删除?

使用 std::thread 在 C++ 中的单独线程中执行每个对象

C++ 多线程std::thread 详解

C++ 多线程std::thread 详解