- 操作系统:ubuntu22.04
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
std::thread 是 C++11 标准库中用于创建和管理线程的核心类,定义在 头文件中。它使得多线程编程变得简单、类型安全且跨平台。
一、std::thread 简介
std::thread 是一个类,代表一个执行线程。你可以用它来启动一个函数(普通函数、lambda、函数对象、成员函数等)在新的线程中运行。
🔧 所需头文件:
cpp
#include <thread>
📦 常用成员函数:
函数 | 说明 |
---|---|
join() | 等待线程执行完毕(阻塞主线程) |
detach() | 分离线程,让其在后台独立运行 |
get_id() | 获取线程的唯一 ID |
joinable() | 判断线程是否可以 join 或 detach |
swap() | 交换两个 thread 对象 |
native_handle() | 获取底层平台的原生线程句柄(如 pthread_t) |
二、创建线程的多种方式(附示例)
- 使用普通函数
cpp
#include <iostream>
#include <thread>
#include <chrono>
void hello()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl;
}
int main()
{
std::thread t(hello); // 启动线程执行 hello 函数
std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
t.join(); // 等待线程结束
std::cout << "Thread joined." << std::endl;
return 0;
}
输出示例:
bash
Main thread ID: 1
Hello from thread 2
Thread joined.
- 使用带参数的函数
cpp
void print_message(const std::string& msg, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << msg << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
int main()
{
std::thread t(print_message, "Hello from thread!", 3);
std::cout << "Main thread is running..." << std::endl;
t.join();
std::cout << "Thread finished." << std::endl;
return 0;
}
⚠️ 注意:参数是值传递的。如果要传引用,必须用 std::ref。
✅ 正确传递引用:
cpp
void increment(int& x)
{
++x;
}
int main()
{
int a = 0;
std::thread t(increment, std::ref(a)); // 使用 std::ref 传引用
t.join();
std::cout << "a = " << a << std::endl; // 输出: a = 1
return 0;
}
✅ 3. 使用 Lambda 表达式
cpp
int main()
{
int local = 10;
std::thread t([local]()
{
// 捕获 local 的副本
std::cout << "Lambda: local = " << local << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
});
t.join();
return 0;
}
提示:如果要修改捕获的变量,使用 mutable 或传引用:
cpp
std::thread t([&local]() mutable
{
local += 5;
std::cout << "Modified: " << local << std::endl;
});
✅ 4. 使用函数对象(仿函数)
cpp
struct Task
{
void operator()() const
{
std::cout << "Task executed in thread " << std::this_thread::get_id() << std::endl;
}
};
int main()
{
std::thread t(Task{});
t.join();
return 0;
}
✅ 5. 使用类的成员函数
cpp
class Worker
{
public:
void work(int id)
{
std::cout << "Worker " << id << " working in thread " << std::this_thread::get_id() << std::endl;
}
};
int main()
{
Worker w;
std::thread t(&Worker::work, &w, 42); // &w 是对象指针,42 是参数
t.join();
return 0;
}
🔍 解释:&Worker::work 是成员函数指针,&w 是对象地址,42 是参数。
三、join() vs detach()
❗ 每个 std::thread 对象在析构前必须被 join 或 detach,否则程序会 std::terminate()。
✅ join():等待线程结束
cpp
std::thread t(some_function);
t.join(); // 主线程阻塞,直到 t 结束
// 此时 t 不再可 join,t.joinable() 返回 false
✅ detach():分离线程
cpp
std::thread t(some_function);
t.detach(); // 线程在后台运行,不再与 thread 对象关联
// 不能再 join,线程生命周期由系统管理
⚠️ 分离线程的风险:如果主线程结束,整个程序终止,分离线程也会被强制终止。
🧪 四、检查线程状态:joinable()
cpp
std::thread t(some_function);
if (t.joinable())
{
t.join(); // 安全调用
}
常用于异常安全代码中,确保线程被正确处理。
🧰 五、线程 ID 与当前线程操作
cpp
#include <iostream>
#include <thread>
void show_id()
{
std::cout << "This thread ID: " << std::this_thread::get_id() << std::endl;
}
int main()
{
std::thread t(show_id);
std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
t.join();
// 让出 CPU 时间片
std::this_thread::yield();
// 休眠 500 毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return 0;
}
🧱 六、线程安全与共享数据(简单示例)
cpp
#include <iostream>
#include <thread>
#include <mutex>
int counter = 0;
std::mutex mtx;
void increment()
{
for (int i = 0; i < 100000; ++i)
{
std::lock_guard<std::mutex> lock(mtx);
++counter;
}
}
int main()
{
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final counter: " << counter << std::endl; // 应为 200000
return 0;
}
🔐 使用 std::mutex 和 std::lock_guard 保护共享变量 counter。
🚫 七、常见错误与注意事项
❌ 错误1:未调用 join 或 detach
cpp
int main()
{
std::thread t([]{ std::cout << "Hello"; });
return 0; // t 析构时未 join/detach → 调用 std::terminate()
}
✅ 正确做法:
cpp
std::thread t(...);
// ...
t.join(); // 或 t.detach();
❌ 错误2:传递指针或引用时对象已销毁
cpp
std::thread t(increment, std::ref(local_var));
// 如果 local_var 是局部变量且线程未结束,可能访问已销毁内存
✅ 建议:确保线程使用的数据生命周期长于线程本身。
✅ 八、完整示例:多线程并行计算
cpp
#include <iostream>
#include <vector>
#include <thread>
#include <numeric>
void accumulate(const std::vector<int>& vec, int start, int end, int& result)
{
result = std::accumulate(vec.begin() + start, vec.begin() + end, 0);
}
int main()
{
std::vector<int> data(100000, 1); // 10万个1
int sum1 = 0, sum2 = 0;
std::thread t1(accumulate, std::ref(data), 0, 50000, std::ref(sum1));
std::thread t2(accumulate, std::ref(data), 50000, 100000, std::ref(sum2));
t1.join();
t2.join();
std::cout << "Total sum: " << sum1 + sum2 << std::endl; // 100000
return 0;
}
总结:std::thread 核心要点
要点 | 说明 |
---|---|
✅ 启动线程 | std::thread t(func, args...) |
✅ 等待结束 | t.join() |
✅ 后台运行 | t.detach() |
✅ 安全检查 | t.joinable() |
✅ 线程ID | t.get_id() 或 std::this_thread::get_id() |
✅ 传引用 | 使用 std::ref() |
✅ 异常安全 | 在异常路径中也要 join/detach |
掌握 std::thread 是学习 C++ 多线程的第一步。结合 mutex、condition_variable 和 future,你就能构建出高效、安全的并发程序。建议多写小例子练习传参、生命周期管理、同步等关键点。