如何在C++中创建和管理线程

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()用在后台监控、日志写入等无需交互的任务。

相关推荐
努力实干12 小时前
面向对象设计原则——C++
开发语言·c++·数码相机
syagain_zsx15 小时前
jsoncpp 使用指南:编译链接、Json::Value 取值与高频坑
c++·json·库·http格式
布莱克60516 小时前
理解 = delete 与 = default:两种赋值语义的差异
c++
小小、码农17 小时前
[特殊字符]【C++11】类的新功能全面解析:移动构造、移动赋值、default、delete、final 与 override
linux·开发语言·网络·c++
库玛西17 小时前
外部排序全解析:败者树、置换-选择排序与最佳归并树
c语言·数据结构·c++·笔记·考研·算法
General_G19 小时前
创建型模式—原型(Prototype)模式
c++·设计模式·原型模式
syagain_zsx19 小时前
protobuf 实战:从 .proto 到 C++ 序列化的完整链路
c++·学习·程序人生·谷歌·protobuf
welfare96220 小时前
新号别搞:C++_list
开发语言·c++
xiaoye-duck20 小时前
《C++ 项目:手写大模型接入 SDK》云端 API 调用基础与 Apifox 调试实战
网络·c++·语言模型
坤坤子吖20 小时前
Linux 线程同步与互斥——条件变量与线程同步
linux·开发语言·c++·笔记·学习