C++11引入了<thread>头文件,提供了标准化的线程操作接口,使得多线程编程变得更加便捷和可移植
现代C++的线程管理以RAII 为核心原则,C++20引入的jthread进一步完善了自动管理机制
1. 创建线程(std::thread)
- 线程对象:
std::thread代表一个可执行的线程,可以传递函数指针、lambda表达式、仿函数等作为线程的执行内容 - 核心原则:
std::thread对象在销毁前必须调用join()/detach()函数,否则程序会直接终止(std::terminate)
2. 线程的生命周期管理
join():主线程调用join()来等待新线程的结束。join()确保主线程等待子线程完成,避免主线程提前结束,导致子线程被销毁。
cpp
#include <iostream>
#include <thread>
static bool s_Finished = false;//所有线程共享的资源
void DoWork()
{
using namespace std::literals::chrono_literals;
std::cout << "Started thread id=" << std::this_thread::get_id() << std::endl;
while (!s_Finished)//当其它线程将s_Finished设为true时,该线程结束
{
std::cout << "Working...\n";
std::this_thread::sleep_for(1s);
}
}
int main()
{
std::thread worker(DoWork);//子线程开始执行
std::cin.get();//主线程等待用户按下回车(主线程停滞)
s_Finished = true;//用户按下回车后,主线程将s_Finished设为true
worker.join();//主线程等待子线程worker结束
std::cout << "Finished" << std::endl;
std::cin.get();
}
detach():将线程与线程对象完全分离,不再受主线程或原线程对象的控制,线程在后台独立运行,直至线程结束。执行完成后,自动释放所有资源。
cpp
#include <iostream>
#include <string>
#include <thread>
void bgTask()
{
for (int i = 0; i < 5; ++i)
{
std::cout << "background running..." << i << std::endl;
}
std::cout << "Finished" << std::endl;
}
int main()
{
//将该线程与线程对象完全分离,不在首主线程或原线程对象的控制
//该线程在后台独立运行
std::thread(bgTask).detach();
//主线程退出会杀死所有分离线程,必须给足够的运行时间
std::cin.get();//主线程停滞
}
3. 线程的返回值
std::thread本身不支持返回值。
C++标准库提供了获取线程返回值的多种方式:
std::async会自动创建一个线程并返回一个future对象,对future对象调用get()函数可以阻塞等待并获取返回值
cpp
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
int add(int a, int b)
{
std::cout << "Started thread id=" << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return a + b;
}
int main()
{
//异步执行,返回future
std::future<int> result = std::async(add, 10, 20);
//主线程停滞,等待获取线程的返回结果
std::cout << "Result: " << result.get() << std::endl;
std::cin.get();
std::cout << "main thread Finished" << std::endl;
}
在C++中,我们通过
std::thread创建线程,通过传入函数指针,Lambda表达式,类成员函数等 来指定线程的执行内容。通过
join()/detach()来管理类的生命周期,join()确保主线程等待子线程的结束,避免主线程提前结束,导致子线程被销毁;detach()将线程与线程对象完全分离,线程独立运行在后台,线程结束后自动释放所有资源,分离的线程由C++运行时托管,无法获取到其状态或结果,也无法再管理其生命周期(确保给足够的运行时间,主线程结束会杀死所有分离线程)。如果不对一个线程对象调用
join()/detach(),C++会直接终止程序,这也是C++防止线程泄漏的手段。在C++20中,更推荐使用
jthread,它在线程对象析构时,自动调用join(),避免了手动调用join()的潜在风险。多线程环境下,共享数据必须通过同步机制保护------最常见的是
std::mutex配合std::lock_guard实现RAII 式加锁保护,或者使用std::atomic实现无锁并发编程。对于简单异步任务,使用
std::async封装比手动管理线程更加高效,它能自动处理结果返回和异常传递。总的来说,C++线程管理的核心在于严格遵守"创建-同步-回收"的生命周期,优先使用现代工具,如
jthread和std::async,并始终要确保共享数据的线程安全
面试官可能追问
- 为什么
detach()后无法管理线程的生命周期?
答:detach()后,线程与线程对象完全分离,该线程由C++运行时托管,无法再获取其状态和结果,也无法管理其生命周期。
- 何时用
join(),何时用detach()?
答:join()用在需要等待的场景;detach()用在后台监控、日志写入等无需交互的任务。