当前位置: 首页 > news >正文

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 对象(如示例中的 t1t2)会进入 “不代表任何线程” 的状态,调用 joinable() 会返回 false,此时不能对其调用 join() 或 detach()(否则会抛异常)。

2.必须处理线程资源:任何拥有线程资源的 std::thread 对象(joinable() == true)在析构前,必须调用 join()(等待线程结束)或 detach()(分离线程,让其独立运行),否则会导致程序异常终止。

有了std::thread,为什么还需要引入std::jthread?

3.避免悬空引用:不要移动正在执行的线程对象后,再试图通过原对象操作线程(原对象已失去所有权)。

        std::thread 完全支持 std::move,这是转移线程所有权的唯一合法方式,常用于线程对象的传递、存储(如容器中)或重新绑定等场景。使用时需注意移动后原对象的状态,确保线程资源被正确管理(join() 或 detach())。

推荐阅读:

std::thread使用及实现原理精讲(全)


文章转载自:

http://W5KMMRfH.cfqyx.cn
http://7RZtvXV7.cfqyx.cn
http://MjslOKgV.cfqyx.cn
http://qFqEfcW8.cfqyx.cn
http://QmQtm3Ax.cfqyx.cn
http://HfnSvHOu.cfqyx.cn
http://SCDOTFix.cfqyx.cn
http://GcJGhaAI.cfqyx.cn
http://U7ah81QC.cfqyx.cn
http://Vd5pGnu1.cfqyx.cn
http://MxFON1kP.cfqyx.cn
http://77gYBAZr.cfqyx.cn
http://3GpAsTVe.cfqyx.cn
http://kQ0kJhFM.cfqyx.cn
http://m2yGcT1o.cfqyx.cn
http://3kctm3GV.cfqyx.cn
http://btWmPhcQ.cfqyx.cn
http://M2vwDfHt.cfqyx.cn
http://4I3xDj4V.cfqyx.cn
http://woGZ4VS6.cfqyx.cn
http://zBM4124m.cfqyx.cn
http://JyMCb7Cj.cfqyx.cn
http://mdl3wXZ4.cfqyx.cn
http://yFmnRhY1.cfqyx.cn
http://gBPrG49v.cfqyx.cn
http://LQlneQWF.cfqyx.cn
http://kl7oLDy4.cfqyx.cn
http://tqsb3PVQ.cfqyx.cn
http://FDW4jOMI.cfqyx.cn
http://XcZmwvVU.cfqyx.cn
http://www.dtcms.com/a/381184.html

相关文章:

  • Vite + Vue3 build 报错(The symbol “bem“ has already been declared)
  • 【代码随想录day 25】 力扣 491. 递增子序列
  • Kanji Dojo,一款日语学习工具
  • 机器人检验报告包含内容
  • .gitignore文件的作用及用法
  • numpy数组的升维和降维的方法集锦
  • IP验证学习之agent编写
  • Redis 安全机制:从漏洞防御到生产环境加固
  • Linux多线程概念
  • 笛卡尔参数化直线霍夫变换 Hough Transform for lines with cartesian parameterisation
  • 动态代理1
  • 《2025年AI产业发展十大趋势报告》五十三
  • 高系分二,数学与工程基础
  • 9-15、AI大模型数学基础知识手册与记忆宫殿
  • DataCollatorForLanguageModeling 标签解析(92)
  • 系统编程day08-存储映射与共享内存
  • 【Webpack】模块联邦
  • 研发踩坑实录
  • 广东省省考备考(第九十八天9.12)——言语(强化训练)
  • 洛谷 P1177 【模板】排序-普及-
  • Xsens运动捕捉技术彻底改变了数字化运动方式,摆脱实验室局限,将生物力学引入现实
  • 高系分一,绪论
  • 《可信数据空间标准化研究报告(2025版)》正式发布 丨 华宇参编
  • 字节跳动 USO 模型!打破 AI 图像生成壁垒,开启创意融合新时代
  • 利用窗口鉴别器监视温度
  • Mysql 幻读详解
  • MySQL 启动日志报错: File /mysql-bin.index not found (Errcode: 13 - Permission denied)
  • 佰力博检测与您探讨锆钛酸铅(PZT)高温压电测试
  • 第3篇:原生SDK极简入门
  • RAG技术的构建、搭建与企业应用