如何在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++线程管理的核心在于严格遵守"创建-同步-回收"的生命周期,优先使用现代工具,如jthreadstd::async,并始终要确保共享数据的线程安全

面试官可能追问

  • 为什么detach()后无法管理线程的生命周期?

答:detach()后,线程与线程对象完全分离,该线程由C++运行时托管,无法再获取其状态和结果,也无法管理其生命周期。

  • 何时用join(),何时用detach() ?

答:join()用在需要等待的场景;detach()用在后台监控、日志写入等无需交互的任务。

相关推荐
雪度娃娃2 小时前
转向现代C++——在意为改写的函数添加 override
开发语言·c++
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之前缀和&差分 --【一维差分】:[NOIP 2018 提高组] 铺设道路
c++·前缀和·差分·csp·高频考点·信奥赛·铺设道路
星马梦缘2 小时前
aaaaa
数据结构·c++·算法
喵星人工作室3 小时前
C++火影忍者1.1.2
开发语言·c++
basketball6163 小时前
C++ 中的 ptrdiff_t 详解
开发语言·c++
wunaiqiezixin3 小时前
互斥锁与自旋锁的区别
c++
代码中介商4 小时前
深入解析STL中的stack、queue与priority_queue
开发语言·c++
磊 子5 小时前
STL无序关联容器—unorded_set+unorded_map
开发语言·c++
初夏睡觉5 小时前
数据结构学习之~二叉堆 (P3378 【模版】堆)
数据结构·c++·学习