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

相关推荐
clint4563 天前
C++进阶(1)——前景提要
c++
夜悊3 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴3 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0014 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
玖玥拾4 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
один but you4 天前
constexpr函数
c++
凡人叶枫4 天前
Effective C++ 条款41:了解隐式接口和编译期多态
java·开发语言·c++·effective c++
凡人叶枫4 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
小胖xiaopangss4 天前
BRpc使用
c++·rpc
-森屿安年-4 天前
63. 不同路径 II
c++·算法·动态规划