C++ 多线程库 thread

下面对 C++11 及以后标准中 <thread> 头文件所提供的多线程支持做一次系统、深入的梳理,包括线程管理类、线程局部存储、线程休眠与让渡、硬件并发查询,以及实践建议与注意事项。


一、概述

  • <thread> 是 C++ 标准库提供的跨平台线程支持,封装了操作系统原生线程接口。
  • 核心类型与函数分布在三个命名空间:
    • std::thread:线程类
    • std::this_thread:当前线程相关操作
    • std::hardware_concurrency:并发度查询

二、std::thread 类

1. 定义与构造

namespace std {
  class thread {
  public:
    // 构造
    thread() noexcept;  // 不代表任何线程
    template<class F, class... Args>
    explicit thread(F&& f, Args&&... args);
      // 启动新线程,调用 std::invoke(f, args...)
  …
  };
}
  • 无参构造:创建“空”线程对象,不代表任何执行单元。
  • 函数对象构造:将可调用对象(函数指针、lambda、函数对象)和其参数拷贝/移动到新线程中执行。

2. 成员函数

函数功能
join()阻塞当前线程直到该 std::thread 完成;必须可 join,否则 std::terminate
detach()分离线程——线程完成后自主释放资源;调用后不可再 join() 或 detach()
joinable()判断是否为可 join 状态(即已关联且未 join/detach)。
get_id()返回 std::thread::id,可用于标识或比较线程。
native_handle()获取底层原生线程句柄,可用于平台特定调优或 API 调用。
#include <thread>
#include <iostream>

void worker(int id) {
    std::cout << "Worker " << id << " start\n";
    // … 
    std::cout << "Worker " << id << " end\n";
}

int main() {
    std::thread t1(worker, 1);
    if (t1.joinable()) t1.join();
}
  • 注意:若一个 std::thread 对象在析构时仍为 joinable,会调用 std::terminate——务必在析构前 join() 或 detach()

三、std::this_thread 命名空间

提供当前线程的工具函数。

函数功能
this_thread::get_id()返回当前线程的 std::thread::id
this_thread::yield()暂让当前线程执行权,使调度器有机会切换到其他就绪线程。
this_thread::sleep_for(d)阻塞当前线程持续指定 std::chrono::duration
this_thread::sleep_until(tp)阻塞当前线程直到指定 std::chrono::time_point
#include <thread>
#include <chrono>

std::this_thread::sleep_for(std::chrono::milliseconds(100));
  • yield() 常用于自旋锁或忙等循环中,让出 CPU。
  • 睡眠 精度受操作系统调度影响,不保证严格时长。

四、线程局部存储(thread_local

  • C++11 引入了 thread_local 存储修饰符,用于声明每个线程独有的一份变量副本。
  • 对比:static 是进程内单例,thread_local 是每个线程单例。
thread_local int counter = 0;

void func() {
    ++counter;
    // 每个线程都有自己独立的 counter
}
  • 应用场景:避免对共享数据加锁,存放线程私有的缓存、状态或错误码等。

五、并发度查询

unsigned n = std::thread::hardware_concurrency();
  • 返回系统建议的并行线程数量,通常等于逻辑 CPU 核心数。
  • 若实现不支持或无法检测,可能返回 0。

六、实践建议与注意事项

  1. RAII 管理线程
    • 推荐封装一个小型的线程守护类,在析构时自动 join() 或 detach(),避免忘记管理:struct ThreadGuard { std::thread t; ~ThreadGuard() { if (t.joinable()) t.join(); } };
  2. 避免数据竞争
    • 对多个线程访问同一可变数据时,使用互斥量(<mutex>)、读写锁或原子操作(<atomic>)保护。
  3. 慎用 detach()
    • detach() 后你无法知道线程何时完成,也无法获取其返回值或异常——仅在后台运行独立任务、且不关心其结果时使用。
  4. 线程参数传递
    • std::thread 构造时会复制或移动参数;如需传引用,必须使用 std::ref()int x = 0; std::thread t([](int& v){ v = 1; }, std::ref(x));
  5. 控制并发度
    • 根据 hardware_concurrency 限制同时运行线程数量,避免过度切换开销。
    • 可用线程池复用线程(标准库尚未提供,建议使用第三方或自行实现)。
  6. 异常安全
    • 线程函数若抛异常,若未捕获会导致 std::terminate,可在函数内捕捉所有异常并传回主线程或记录日志。
  7. 线程亲和性(Affinity)
    • 如需绑定线程到特定 CPU,需调用 native_handle() 并使用平台 API(如 pthread 或 WinAPI)设置。
  8. 使用高层并行框架
    • 对于大量并行任务,优先考虑 <future>/<async>、并行算法(<algorithm> + <execution>)、或第三方线程池,避免直接管理原始线程过多细节。

通过以上对 <thread> 的详尽讲解与实践建议,你可以更安全、高效地在现代 C++ 中组织与管理多线程任务。祝编码顺利!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注