线程(2)

**`std::thread` 的基础用法:**

– **构造函数:**
– `std::thread(Function, Args…)`: 接受一个函数和其参数来创建并启动线程。
– **拷贝构造被禁用 (Deleted):** `std::thread t2 = t1;` 是非法的,线程对象不能被拷贝。
– **支持移动构造 (Move Constructor):** `std::thread t2 = std::move(t1);` 是合法的,所有权转移。
– **参数传递陷阱:**
– **默认按值拷贝:** 即使函数参数声明为引用,`std::thread` 构造函数也会默认拷贝参数。
– **传引用需用 `std::ref`:** 如果想真正传递引用(在线程内修改外部变量),必须使用 `std::ref(var)` 包装参数。
– **成员函数调用:**
– 语法:`std::thread(&ClassName::MethodName, &object, args…)`。
– 必须传入**对象指针**(或地址)作为第二个参数,以确定在这个对象上调用该方法。
– **线程管理:**
– `join()`: 阻塞等待线程结束。
– `detach()`: 分离线程,使其在后台运行(需注意生命周期问题)。
– `get_id()`: 获取线程 ID。

### 1. `detach()` 的“坑”:主线程退出,全员陪葬

**现象:**运行代码,发现子线程 `func5` 里的打印语句并没有输出,程序就结束了。 **核心原理:**

– **非独立进程:** `detach()` **不会**把线程变成一个独立的进程。它只是把线程对象 (`std::thread t`) 和后台运行的线程执行流“断开联系”。
– **共存亡:** 子线程依然属于当前进程。如果**主线程(Main Thread)结束**,整个进程(Process)就会销毁,操作系统会强行杀死该进程下所有的线程,无论它们是否被 detach。
– **资源泄漏风险:** 讲师提到如果 detach 的线程还在访问某些资源(如文件、内存),主线程退出导致的强制终止可能导致资源未正确释放。

**代码演示:**

“`C++
void func5() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时
std::cout << “Thread 5 working…” << std::endl; // 这句可能来不及打印
}

int main() {
std::thread t5(func5);
t5.detach(); // 分离线程,让它在后台跑

// 主线程立刻结束
return 0;
// 结果:进程销毁,后台的 t5 还没醒过来就被系统杀死了。
}
“`

### 2. `detach` 后严禁 `join`

**现象:** 尝试先 `detach()`,然后紧接着调用 `join()`,结果程序抛出“系统错误”(System Error)并崩溃。 **核心原理:**

– `std::thread` 对象只是一个**句柄(Handle)**。
– 当你调用 `detach()` 后,这个句柄就和物理线程**断绝关系**了。
– 此时 `t5` 变成了一个空的壳子(Empty Shell)。你不能对一个空壳子调用 `join()`,这属于非法操作。

**代码演示:**

“`C++
std::thread t5(func5);
t5.detach(); // t5 不再持有线程

if (t5.joinable()) {
t5.join();
} else {
// detach 后,joinable() 变为 false
// 再次 join 会 crash
// t5.join(); // ❌ 崩溃:Invalid Argument
}
“`

### 3. `joinable()` 的判断作用

**现象:** 调用 `t5.joinable()`,打印结果为 `0` (false)。 **最佳实践:** 在调用 `join()` 或 `detach()` 之前,**永远建议**先检查 `joinable()`。

– 一个默认构造的线程 `std::thread t;` 是不可 join 的。
– 一个已经 `join()` 过的线程是不可 join 的。
– 一个已经 `detach()` 过的线程是不可 join 的。

### 4. 线程所有权的转移 (`std::move`)

**现象:** 演示了 `t6_2 = std::move(t6_1)`,然后尝试操作 `t6_1`,导致异常。 **核心原理:**

– `std::thread` 是**独占资源**(类似 `unique_ptr`)。一个物理线程只能被一个对象管理。
– `std::move` 发生了**所有权转移**:
– `t6_2` 拿到了线程的控制权。
– `t6_1` 变成了空壳(Null)。
– **后果:** 转移后,原对象 `t6_1` 就不能再进行任何线程操作(如 join/detach/get_id),否则会报错或行为未定义。

**代码演示:**

“`C++
void func() {}

int main() {
std::thread t1(func);

// t2 接管 t1 的线程,t1 变空
std::thread t2 = std::move(t1);

// t1.join(); // ❌ 错误:t1 已经空了
t2.join(); // ✅ 正确:t2 现在负责管理线程
}
“`

发表评论