我在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
的析构函数是如何被调用的。
1条答案
按热度按时间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
来避免此类问题。