2023年12月23日

C++11 新特性:多线程管理 std::thread


C++11 引入了对多线程编程的支持,其中 std::thread 类是多线程功能的核心。以下是对 std::thread 的详细介绍,包括其用法、线程管理方法以及底层实现细节。


1. 基础概念

std::thread 是 C++11 引入的用于创建和管理线程的类。它提供了一种简单的方式来并行执行代码,使得 C++ 支持多线程编程。

创建线程

#include <iostream>#include <thread>void hello() { std::cout << "Hello from thread!" << std::endl;}int main() { std::thread t(hello); // 创建一个线程执行 hello 函数 t.join(); // 等待线程 t 完成 return 0;}

2. 线程函数

线程函数是线程启动时执行的代码。线程函数可以是普通函数、lambda 表达式、函数对象(即带有 operator() 的类),或是绑定了参数的函数。

函数对象(Lambda 表达式)

#include <iostream>#include <thread>int main() { int x = 10; std::thread t([x]() { std::cout << "Value of x: " << x << std::endl; }); t.join(); return 0;}

函数对象(类)

#include <iostream>#include <thread>class Functor {public: void operator()() const { std::cout << "Functor called!" << std::endl; }};int main() { std::thread t(Functor()); t.join(); return 0;}

3. 线程管理

joindetach 是管理线程生命周期的两种主要方法。

  • join

    • 功能:阻塞当前线程,直到目标线程完成执行。
    • 用法:在调用 join 后,调用线程会等待目标线程结束,然后恢复执行。
    • 底层实现:使用同步机制(如互斥量和条件变量)来等待线程完成。

    std::thread t(someFunction); t.join(); // 等待线程 t 完成

  • detach

    • 功能:将线程从主线程分离,使其在后台运行,不再需要主线程等待。
    • 用法:线程一旦 detach,它会在后台继续执行,直到完成。
    • 底层实现:通过操作系统的线程调度和资源管理机制来管理线程。

    std::thread t(someFunction); t.detach(); // 线程 t 在后台继续执行

4. 底层实现

std::thread 的底层实现涉及与操作系统线程库的交互。在 Linux 系统上,std::thread 通常使用 POSIX 线程库(pthread)。以下是关于 std::thread 的底层实现的详细解读:

4.1. std::thread 的创建

std::thread 构造函数

#include <iostream>#include <pthread.h>#include <functional>// 简化版 std::thread 实现class MyThread {public: // 构造函数接受一个可调用对象 template <typename Callable> MyThread(Callable&& func) : started(false), joined(false) { // 创建线程并执行函数 int result = pthread_create(&thread, nullptr, threadFuncWrapper<Callable>, new Callable(std::forward<Callable>(func))); if (result != 0) { throw std::runtime_error("Failed to create thread"); } started = true; } // 析构函数,确保线程结束前资源得到释放 ~MyThread() { if (started && !joined) { pthread_detach(thread); // 线程分离 } } // 等待线程结束 void join() { if (started && !joined) { pthread_join(thread, nullptr); joined = true; } } // 分离线程 void detach() { if (started && !joined) { pthread_detach(thread); joined = true; } }private: pthread_t thread; // POSIX 线程句柄 bool started; // 标记线程是否已启动 bool joined; // 标记线程是否已加入 // 线程函数包装器 template <typename Callable> static void* threadFuncWrapper(void* arg) { Callable* func = static_cast<Callable*>(arg); (*func)(); // 执行函数 delete func; // 释放 Callable 对象 return nullptr; }};

线程函数包装

template <typename Callable>void* threadFuncWrapper(void* arg) { auto func = *static_cast<std::shared_ptr<Callable>*>(arg); (*func)(); // 执行可调用对象 return nullptr;}
4.2. pthread_create 的调用
  • 第一个参数:是线程的句柄(pthread_t 类型)的地址。

  • 第二个参数:线程属性(通常设置为 nullptr 表示默认属性)。

  • 第三个参数:线程函数指针,通常是 C 风格函数(例如 threadFuncWrapper)。

  • 第四个参数:传递给线程函数的参数,这里是指向 Callable 对象的 void* 指针。

pthread_create(&threadHandle, nullptr, threadFuncWrapper<Callable>, new Callable(std::forward<Callable>(func)));
  • 底层实现
    • 创建线程pthread_create 在内部创建一个新线程,并开始执行指定的线程函数(即 threadFuncWrapper)。

    • 传递参数:将 Callable 对象的指针转换为 void* 传递,线程函数内部再将其转换回 Callable 指针并调用。

4.3. joindetach 的底层实现
  • join

    • 功能:阻塞当前线程,直到目标线程完成。底层通过同步机制(如互斥量和条件变量)实现等待。
    • 实现:在 pthread 中,pthread_join 会阻塞调用线程,直到目标线程完成执行。

    pthread_join(threadHandle, nullptr);

  • detach

    • 功能:使线程在后台独立运行。操作系统负责线程的清理和资源回收。
    • 实现:在 pthread 中,pthread_detach 将线程设为分离状态,线程结束后,操作系统会自动清理资源。

    pthread_detach(threadHandle);


5. joindetach 的具体区别

  • join

    • 将当前线程阻塞,等待目标线程完成。

    • 线程必须在 join 之前是可 join 的。

  • detach

    • 将线程从主线程分离,允许它在后台独立运行。

    • 分离后的线程不会被主线程管理,必须小心资源管理以防泄漏。

注意:线程一旦被 detach,它变成独立线程,主线程无法直接控制或跟踪其状态。确保线程在 detach 后完成执行,否则可能导致资源泄漏或未定义行为。


总结

std::thread 提供了 C++11 中强大的多线程支持,允许在应用程序中实现并行执行。通过 joindetach,你可以灵活地管理线程的生命周期,选择在需要时等待线程完成或将线程置于后台独立运行。底层实现通常依赖于 POSIX 线程库,通过封装和类型安全地支持了复杂的线程管理和操作。