c++ 线程的析构函数何时被调用?

jchrr9hc  于 2023-04-08  发布在  其他
关注(0)|答案(1)|浏览(130)

我在Bjarne Stroustrup的“The C++ programming language”中看到了一个基本的std::thread用法的例子,它让我感到困惑。下面是这个例子:

void run(int i, int n) // warning: really poor code
{
    thread t1 {f};
    thread t2;
    vector<Foo> v;
    // ...
    if (i<n)
    {
        thread t3 {g};
        // ...
        t2 = move(t3); // move t3 to outer scope
    }
    v[i] = Foo{}; // might throw
    // ...
    t1.join();
    t2.join();
}

正如Stroustrup所写:
我们可能永远不会到达最后的两个join()。在这种情况下,t1的析构函数将终止程序。
但是,在哪种情况下调用t1的析构函数?t1线程并没有超出它的作用域。也没有显式的delete调用。
我试着在适当的修改下运行这段代码,但仍然找不到t1的析构函数是如何被调用的。

oxalkeyp

oxalkeyp1#

但是,在哪种情况下调用t1的析构函数呢?t1线程并没有超出它的作用域,也没有显式的delete调用。
代码中明确指出v[i] = Foo{};行可能会抛出异常。如果发生这种情况,控制将永远不会到达同一块作用域中的t1.join();行,但该作用域的所有局部变量,包括t1,仍然会被销毁,并调用其析构函数。
std::thread的析构函数被调用,而线程既没有加入也没有分离,那么析构函数将调用std::terminate,默认情况下,通过调用std::abort终止整个程序。
更一般地说,除非你能确定在.join调用之前没有任何东西会抛出异常,否则代码就被破坏了。如果你不能确定这一点,那么你必须把它 Package 在try { /*...*/ } catch(...) { t1.join(); throw; }中。从技术上讲,你也需要对t2单独做同样的事情,因为t1.join()也允许抛出异常。尽管在这种情况下,你的程序无论如何都将有很大的问题无法继续执行。
在C++20中,可以使用std::jthread来避免此类问题。

相关问题