C++11新特性_标准库_线程库_std::thread
C++11 引入了 <thread>
头文件,提供了 std::thread
类用于创建和管理线程。以下是完整的代码示例,涵盖线程的创建、参数传递、等待线程结束(join
)、分离线程(detach
)以及异常安全管理。
示例 1:基础线程创建与 join
#include <iostream>
#include <thread>
#include <chrono> // 用于时间延迟// 普通函数作为线程入口
void thread_func(int id, const std::string& msg) {for (int i = 0; i < 3; ++i) {std::cout << "线程 " << id << ": " << msg << " (第" << i+1 << "次)" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(200)); // 线程休眠 200ms}
}int main() {// 创建线程,传递参数(值传递)std::thread t1(thread_func, 1, "Hello from t1");std::thread t2(thread_func, 2, "Hi from t2");std::cout << "主线程等待子线程完成..." << std::endl;// 等待线程执行完成(阻塞主线程)t1.join();t2.join();std::cout << "所有子线程完成,主线程结束" << std::endl;return 0;
}
输出说明
主线程等待子线程完成...
线程 1: Hello from t1 (第1次)
线程 2: Hi from t2 (第1次)
线程 1: Hello from t1 (第2次)
线程 2: Hi from t2 (第2次)
线程 1: Hello from t1 (第3次)
线程 2: Hi from t2 (第3次)
所有子线程完成,主线程结束
关键点
- 线程创建:
std::thread t(func, args...)
构造函数接受线程入口函数和参数(参数默认值传递)。 join()
:主线程调用t.join()
会阻塞,直到子线程t
执行完毕。若不调用join()
,程序结束时若线程未完成会崩溃(std::terminate
)。
示例 2:传递引用参数与 detach
若需要传递引用参数,需使用 std::ref
包装(否则参数会被拷贝)。detach()
会让线程独立运行,主线程不再管理其生命周期。
#include <iostream>
#include <thread>
#include <string>
#include <functional> // 用于 std::ref// 线程函数(接收引用参数)
void modify_string(std::string& str) {std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟耗时操作str += " (modified by thread)";
}int main() {std::string msg = "Original message";// 传递引用参数(需用 std::ref 包装)std::thread t(modify_string, std::ref(msg));// 分离线程:子线程独立运行,主线程不再等待t.detach();// 主线程继续执行(可能与子线程并行)std::this_thread::sleep_for(std::chrono::milliseconds(200)); // 等待子线程完成修改std::cout << "最终消息: " << msg << std::endl;// 若主线程提前结束,未完成的子线程会被强制终止(不推荐!)return 0;
}
输出说明
最终消息: Original message (modified by thread)
关键点
- 引用传递:
std::ref(msg)
确保线程函数接收的是msg
的引用而非拷贝。 detach()
:分离后线程独立运行,若主线程提前结束(如未等待子线程完成),子线程可能被终止(需谨慎使用)。
示例 3:使用类成员函数与 RAII 管理线程
线程对象在销毁前必须被 join
或 detach
,否则程序终止。可通过 RAII(资源获取即初始化)模式确保线程被正确管理。
#include <iostream>
#include <thread>
#include <stdexcept>class ThreadGuard {
private:std::thread& t; // 引用外部线程对象
public:explicit ThreadGuard(std::thread& thread) : t(thread) {}~ThreadGuard() {if (t.joinable()) { // 仅当线程可 join 时才 joint.join();}}// 禁止拷贝(线程不可复制)ThreadGuard(const ThreadGuard&) = delete;ThreadGuard& operator=(const ThreadGuard&) = delete;
};class MyClass {
public:void member_func(int repeat) {for (int i = 0; i < repeat; ++i) {std::cout << "成员函数线程执行第 " << i+1 << " 次" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));}}
};int main() {MyClass obj;std::thread t(&MyClass::member_func, &obj, 3); // 成员函数需绑定对象指针// 使用 RAII 确保线程被 joinThreadGuard guard(t);// 模拟主线程异常(如抛异常)try {throw std::runtime_error("主线程抛异常");} catch (const std::exception& e) {std::cout << "捕获异常: " << e.what() << std::endl;}// ThreadGuard 析构时自动 join 线程(即使主线程抛异常)return 0;
}
输出说明
成员函数线程执行第 1 次
成员函数线程执行第 2 次
成员函数线程执行第 3 次
捕获异常: 主线程抛异常
关键点
- 成员函数作为线程入口:需绑定类对象指针(
&obj
)和成员函数指针(&MyClass::member_func
)。 - RAII 管理:
ThreadGuard
类在析构时自动join
线程,避免因异常导致线程未join
而程序终止。
示例 4:使用 Lambda 表达式创建线程
C++11 支持使用 Lambda 作为线程入口函数,适合简单的一次性任务。
#include <iostream>
#include <thread>
#include <vector>int main() {std::vector<std::thread> threads; // 存储线程对象的容器// 创建 3 个线程,使用 Lambda 作为入口for (int i = 0; i < 3; ++i) {threads.emplace_back([i]() {std::cout << "Lambda 线程 " << i << " 启动" << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100 * (i+1)));std::cout << "Lambda 线程 " << i << " 结束" << std::endl;});}// 等待所有线程完成for (auto& t : threads) {t.join();}std::cout << "所有 Lambda 线程完成" << std::endl;return 0;
}
输出说明
Lambda 线程 0 启动
Lambda 线程 1 启动
Lambda 线程 2 启动
Lambda 线程 0 结束
Lambda 线程 1 结束
Lambda 线程 2 结束
所有 Lambda 线程完成
注意事项
- 线程所有权:
std::thread
对象是可移动但不可复制的(std::thread t2 = t1
非法,需用t2 = std::move(t1)
)。 joinable()
检查:调用join()
或detach()
前需通过t.joinable()
判断线程是否可操作(避免重复join
或detach
已分离的线程)。- 资源生命周期:若线程引用了外部变量(如局部变量),需确保变量在线程结束前存活(避免悬垂引用)。
- 线程调度:
std::this_thread::yield()
可主动让出 CPU 时间片;std::this_thread::get_id()
可获取线程 ID。
通过以上示例,可以掌握 C++11 std::thread
的核心用法,包括线程创建、参数传递、生命周期管理及异常安全处理。