从函数返回std::thread是否安全?
例如
std::thread getWindowThread(std::function<void()> f){
std::thread t(f);
return t;
}
std::function<void()> func = [](){};
std::thread winT = getWindowThread(func);
winT.join();
从函数返回std::thread是否安全?
例如
std::thread getWindowThread(std::function<void()> f){
std::thread t(f);
return t;
}
std::function<void()> func = [](){};
std::thread winT = getWindowThread(func);
winT.join();
是的,只要函数的返回值用于初始化或分配给另一个std::thread
对象,它就是安全的。
std::thread
析构函数的唯一前提条件是joinable()
为false
:
~thread();
作用:如果joinable()
,则调用terminate()
。否则,没有任何影响。
要使线程可 joinable()
,线程必须具有不等于值初始化 ID 的 ID:
bool joinable() const noexcept;
返回:get_id()
!=id()
。
std::thread
的移动构造函数和移动赋值运算符指定,被移后的线程将具有等于值初始化 ID 的 ID,因此不可 joinable()
。
thread(thread&& x) noexcept;
后置条件:x.get_id() == id()
并且get_id()
返回在构造开始前x.get_id()
的值。
thread& operator=(thread&& x) noexcept;
...
后置条件:x.get_id() == id()
并且get_id()
返回赋值之前x.get_id()
的值。
...
由于 getWindowThread
返回一个函数局部变量,其返回值将从返回的对象进行移动构造,而由 getWindowThread
返回的值将被移动构造为 winT
,因此您的示例是安全的。返回的 thread
对象不再是 joinable
,可以被销毁。
然而,如果返回值不用于初始化或分配给另一个std::thread
对象,则其析构函数将调用std::terminate()
并使您的程序崩溃。因此,我建议在您的函数中添加[[nodiscard]]
属性,以确保您的编译器至少在这种危险情况下发出警告(如果您可以访问C++17功能,否则有特定于编译器的扩展来启用相同的行为):
[[nodiscard]] std::thread getWindowThread(std::function<void()> f) {
std::thread t(f);
return t;
}
看一下std::thread的构造函数。你可以检查到std::thread
的复制构造函数(和复制赋值)已被删除。请参见this了解更多关于C++中移动构造函数
的知识。
当你从函数返回std::thread时,它的返回对象是通过移动构造的,因为函数返回一个具有值类别prvalue的对象,所以你的变量winT
也将被移动构造。
std::thread winT = getWindowThread(func);
这个方法可以正常工作,不会导致未定义的行为。您可能会认为一旦从函数返回std::thread
,它就会被销毁,并且必须释放其资源,也许其线程可能会被销毁。
但是,如前所述,创建在函数中的对象将被移动构造以构造winT
对象。
详细信息: 此链接表示:
如果*this有一个关联的线程(joinable() == true),则调用std::terminate()。
因此,要安全地销毁任何std::thread对象,您需要确保它不可joinable
。joinable()提供了更多的见解:
因此,一个默认构造的线程是不可连接的。执行代码已经完成,但尚未加入的线程仍然被视为活动执行的线程,因此仍然可以连接。
因此,当winT
的移动构造函数构造它时,它会将自己与从您的函数返回的对象交换。之后,您的函数的临时变量将被销毁,并且由于它不可连接,因此可以安全地销毁它。
是的,很安全。那个线程将被移动到winT。