在 std::thread 中运行时,C++ 成员变量的生命周期是多久?

3
#include <iostream>
#include <string>
#include <thread>

using namespace std;

struct safe_thread : public thread
{
    using thread::thread;

    safe_thread& operator=(safe_thread&&) = default;

    ~safe_thread()
    {
        if (joinable())
        {
            join();
        }
    }
};

struct s
{
    safe_thread t;
    std::string text = "for whatever reason, this text will get corrupted";

    s() noexcept
    {
        std::cout << text << '\n'; // it works in constructor as expected
        t = safe_thread{ [this]
                         { long_task(); }};
    }

    void long_task()
    {
        for (int i = 0; i < 500; ++i)
        {
            std::cout << text << '\n'; // the text gets corrupted in here
        }
    }
};

int main()
{
    s s;
}


在以上代码中,text变量在构造函数中可以正确打印。然而,在一个独立的线程中运行的long_task()函数中,该文本会被损坏(在另一台机器上直接崩溃)。为什么会这样?如果在struct s的析构函数中运行safe_thread的析构函数,那么threadtext的生命周期不应该同样持续很长时间吗?也就是说,当在main()处离开s的作用域时,它们都将离开作用域?

你真的想让s()成为noexcept吗?如果线程无法启动,std::thread构造函数可能会抛出异常,如果分配失败,std::thread也可能会抛出std::bad_alloc异常。 - walnut
1个回答

11
你的问题在于声明成员变量的顺序不正确,需要在 s 类内部进行调整。
int main()
{
    s s;
    // here is called dtor of S
}

当析构函数被调用时,数据成员的销毁顺序与它们的声明顺序相反。你可能会遇到以下情况:

safe_thread t; // [2]
std::string text = "for whatever reason, this text will get corrupted"; // [1]

当第一个字符串 [1] 被销毁时,会调用 [2] 线程的析构函数,在这个过程中,你的程序加入。但是随后它访问已被销毁的 text 变量,这是未定义行为。

将顺序更改为:

std::string text = "for whatever reason, this text will get corrupted";
safe_thread t;

采用这种方法,在连接t的同时,text变量仍然可见,不会被删除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接