std::thread是可以被std::move吗?
目录
1.std::thread的std::move
2.std::move线程的常见用法
3.std::thread被std::move后怎么判断有效
4.注意事项
1.std::thread的std::move
C++之std::move(移动语义)
std::thread
支持移动语义(std::move
),因为它的拷贝构造函数和拷贝赋值运算符被显式删除(不可复制),但移动构造函数和移动赋值运算符是允许的(可移动)。这意味着 std::thread
对象的所有权可以通过 std::move
转移,而不能直接复制。
原因是std::thread
管理的是操作系统级别的线程资源(如线程 ID、执行上下文等),这类资源是不可复制的(一个线程不能被 “复制” 出另一个完全相同的线程)。因此,C++ 标准规定 std::thread
只能通过移动语义转移所有权,确保同一线程资源始终被唯一的 std::thread
对象管理,避免资源竞争或重复释放。
关于std::thread支持std::move可以从它的实现源码得知:
thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}thread& operator=(thread&& _Other) noexcept {if (joinable()) {_STD terminate();}_Thr = _STD exchange(_Other._Thr, {});return *this;}thread(const thread&) = delete;thread& operator=(const thread&) = delete;
上述代码实现了右值构造函数和右值赋值函数,禁止了左值构造函数和左值赋值函数。
2.std::move线程的常见用法
1.移动构造:将线程所有权转移给新对象
#include <iostream>
#include <thread>void func() {std::cout << "线程执行\n";
}int main() {// 创建线程t1,执行funcstd::thread t1(func);// 通过std::move将t1的所有权转移给t2std::thread t2 = std::move(t1); // 此时t1不再拥有线程资源(t1.joinable() == false)// t2拥有线程资源,必须调用join()或detach()t2.join(); return 0;
}
2.移动赋值:将线程所有权转移给已存在的对象
#include <iostream>
#include <thread>void func1() { std::cout << "线程1执行\n"; }
void func2() { std::cout << "线程2执行\n"; }int main() {std::thread t1(func1);std::thread t2(func2);// 将t2的所有权转移给t1(t1原有的线程资源会被销毁,需确保已处理)t1 = std::move(t2); // 此时t2不再拥有线程资源,t1拥有原t2的线程资源t1.join(); return 0;
}
3.在容器中存储 std::thread
(依赖移动语义)
由于 std::thread
不可复制,容器(如 std::vector
)中存储线程时,必须通过移动方式插入:
#include <vector>
#include <thread>void func() {}int main() {std::vector<std::thread> threads;// 直接插入临时线程对象(自动触发移动)threads.emplace_back(func); // 移动已存在的线程对象std::thread t(func);threads.push_back(std::move(t)); // 等待所有线程完成for (auto& th : threads) {th.join();}return 0;
}
3.std::thread被std::move后怎么判断有效
在 std::thread
被 std::move
后,判断其是否 “有效”(即是否仍关联着一个可执行的线程资源)的核心方法是使用 std::thread
的成员函数 joinable()
。
joinable的源码如下:
struct _Thrd_t { // thread identifier for Win32void* _Hnd; // Win32 HANDLE_Thrd_id_t _Id;
};
class thread
{...public:_NODISCARD bool joinable() const noexcept {return _Thr._Id != 0;}private:_Thrd_t _Thr;
}
从代码可以看出:
std::thread
对象被std::move
后,原对象会失去对线程资源的所有权,此时joinable()
返回false
(不再关联任何线程)。- 被移动到的目标对象会获得线程资源的所有权,此时
joinable()
返回true
(关联线程资源)。
joinable()
的返回值是判断 std::thread
对象是否关联线程资源的唯一标准:
joinable() == true
:对象有效,关联着一个未被join()
或detach()
的线程。joinable() == false
:对象无效,不关联任何线程(可能是被移动、已join()
/detach()
,或从未关联过线程)。
示例代码:
#include <iostream>
#include <thread>void func() { /* 线程执行的函数 */ }int main() {// 1. 创建线程对象t1,关联线程资源(joinable()为true)std::thread t1(func);std::cout << "t1 移动前 joinable: " << std::boolalpha << t1.joinable() << "\n"; // 输出 true// 2. 通过std::move将t1的线程资源移动给t2std::thread t2 = std::move(t1);// 3. 移动后,原对象t1不再关联线程资源(joinable()为false)std::cout << "t1 移动后 joinable: " << t1.joinable() << "\n"; // 输出 false// 4. 目标对象t2获得线程资源(joinable()为true)std::cout << "t2 移动后 joinable: " << t2.joinable() << "\n"; // 输出 true// 处理t2关联的线程(必须调用join()或detach(),否则程序异常)t2.join();return 0;
}
4.注意事项
1.移动后原对象状态:被移动的 std::thread
对象(如示例中的 t1
、t2
)会进入 “不代表任何线程” 的状态,调用 joinable()
会返回 false
,此时不能对其调用 join()
或 detach()
(否则会抛异常)。
2.必须处理线程资源:任何拥有线程资源的 std::thread
对象(joinable() == true
)在析构前,必须调用 join()
(等待线程结束)或 detach()
(分离线程,让其独立运行),否则会导致程序异常终止。
有了std::thread,为什么还需要引入std::jthread?
3.避免悬空引用:不要移动正在执行的线程对象后,再试图通过原对象操作线程(原对象已失去所有权)。
std::thread
完全支持 std::move
,这是转移线程所有权的唯一合法方式,常用于线程对象的传递、存储(如容器中)或重新绑定等场景。使用时需注意移动后原对象的状态,确保线程资源被正确管理(join()
或 detach()
)。
推荐阅读:
std::thread使用及实现原理精讲(全)