引言
在 C++11 之前,多线程基本依赖平台相关 API(POSIX pthread、Windows CreateThread)。
std::thread 是 C++11 及以后 提供的跨平台线程抽象,特点:
零成本抽象:内部直接映射 pthread / WinThread
RAII 语义:必须显式
join或detach,否则析构terminate()与
future、async、chrono无缝配合
1. 基础知识
1.1 什么是线程?
线程是操作系统能够进行运算调度的最小单位。多线程允许程序同时完成多个任务,提高程序的并发性和响应性。
1.2 C++11之前的多线程
在C++11之前,开发者常用如 pthreads(POSIX 线程)或操作系统特定的API实现多线程,非常繁琐且可移植性差。
1.3 C++11的std::thread
std::thread 封装了线程的创建与管理,极大简化了多线程编程。
2. std::thread 的基本用法
2.1 创建线程
可以通过传递 函数指针、函数对象、lambda表达式、非静态成员函数或静态成员函数 来创建线程。
#include <iostream>
#include <thread>
void PrintHello() {
std::count << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(PrintHello); // 创建线程并启动
t.join(); // 等待线程结束、
return 0;
}
2.2 传递参数
参数会被拷贝到新线程中。
#include <iostream>
#include <thread>
void PrintNum(int num) {
std::cout << "Number: " << num << std::endl;
}
int main() {
int n = 42;
std::thread t(PrintNum, n); // 传递参数
t.join();
return 0;
}
2.3 传递引用参数
需要使用 std::ref 包装引用参数。
#include <iostream>
#include <thread>
void Increment(int &num) {
++num;
}
int main() {
int value = 0;
std::thread t(Increment, std::ref(value));
t.join();
std::count << "Value: " << value << std::endl;
return 0;
}
2.3 使用Lambda表达式
#include <iostream>
#include <thread>
int main() {
std::thread t([]() {
std::count << "Hello from lambda thread!" << std::endl;
});
t.join();
return 0;
}
3. 线程的生命周期管理
3.1 join() 和 detach()
join():主线程等待子线程执行完毕。
detach():线程与主线程分离,后台运行。
#include <iosteam>
#include <thread>
void Task() {
std::count << "Detached thread running!" << std::endl;
}
int main() {
std::thread t(Task);
t.detach(); // 不再受主线程管理
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 0;
}注意:detach() 后线程必须自行管理资源,否则可能导致资源泄漏。
3.2 线程的可联结性
可以通过 joinable() 方法判断线程是否可以 join() 或 detach() 。
std::thread t(Task);
if (t.joinable()) {
t.join();
}
4. 线程安全与数据同步
多线程访问共享数据时,必须保证线程安全。C++11提供了多种同步机制。下面是一个简单的代码示例:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex g_mutex;
int g_counter = 0;
void SafeIncrement() {
std::lock_guard<std::mutex> lock(g_mutex);
++g_counter;
}
int main() {
std::thread t1(SafeIncrement);
std::thread t2(SafeIncrement);
t1.join();
t2.join();
std::cout << "Counter: " << g_counter << std::endl;
return 0;
}
5. 高级用法
5.1 移动线程
线程对象不可拷贝,但是可以移动。
std::thread t1(Task);
std::thread t2 = std::move(t1); // t1 变为不可用5.2 硬件并发数量
使用 std::thread::hardware_concurrency() 获取建议的线程数。
unsigned int n = std::thread::hardware_concurrency();
std::cout << "Hardware concurrency: " << n << std::endl;5.3 线程ID
每个线程有唯一的ID,可通过 get_id() 获取。
std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;6. 注意事项
每个线程对象必须在销毁前
join()或detach(),否则程序会异常终止。避免数据竞争,合理使用锁。
尽量减少锁的粒度,防止性能瓶颈。
推荐使用RAII(如 std::lock_guard)锁管理器,防止忘记释放。
评论区