【C/C++】c++11引入的thread标准库和POSIX的pthread

文章目录

  • thread vs pthread
    • 1 简介
    • 2 使用
    • 3 同步机制
    • 4 线程局部存储(TLS)
    • 5 错误处理
    • 6 线程取消
    • 7 可移植性
    • 8 其他
    • 9 对比

thread vs pthread

1 简介

  • thread

    • C++标准库的一部分;
    • 面向对象设计:通过类 std::thread 封装线程操作,与 C++ 的 RAII(资源获取即初始化)机制深度集成。
    • 类型安全:参数传递和函数签名由编译器检查,避免类型错误。
    • 异常安全:错误时抛出异常(如 std::system_error),符合 C++ 异常处理机制。
  • pthread

    • POSIX标准下的线程库,主要在类unix系统中使用。
    • C 语言函数库:基于函数式接口(如 pthread_create),需要手动管理线程生命周期。
    • 显式资源管理:需手动初始化/销毁互斥锁、条件变量等资源。可能有类型安全问题,eg. 需要将参数转换为void*
    • 错误码返回:通过返回值传递错误码(如 EAGAIN),需程序员显式处理。

2 使用

  • thread

    • 创建
      使用std::thread对象,构造函数传入函数和参数;
    #include 
    void task(int arg) { /* ... */ }
    std::thread t(task, 42); // 自动推断参数类型
    t.join(); // 等待线程结束
    
    • 销毁
      线程对象析构时,若未调用 join() 或 detach(),程序会终止(通过 std::terminate)
  • pthread

    • 创建
      显式调用pthread_create,并传入函数指针和参数
    #include 
    void* task(void* arg) { /* ... */ return NULL; }
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, task, (void*)42); // 需手动转换类型
    pthread_join(tid, NULL); // 等待线程结束
    
    • 销毁
      线程分离需显式调用 pthread_detach,否则可能导致资源泄漏。

3 同步机制

  • thread

    • 提供 std::mutexstd::condition_variable 等 RAII 封装。
    • 支持 std::lock_guardstd::unique_lock 自动管理锁生命周期:
      std::mutex mtx;
      {
          std::lock_guard<std::mutex> lock(mtx); // 自动加锁/解锁
          // 临界区操作
      }
      
  • pthread

    • 需手动初始化和销毁同步原语(如 pthread_mutex_init/pthread_mutex_destroy)。
    • 显式加锁/解锁:
      pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
      pthread_mutex_lock(&mtx);
      // 临界区操作
      pthread_mutex_unlock(&mtx);
      

4 线程局部存储(TLS)

  • thread
    • 使用 thread_local 关键字声明线程局部变量:
      thread_local int counter = 0; // 每个线程独立实例
      
  • pthread
    • 需通过 pthread_key_createpthread_setspecificpthread_getspecific 管理 TLS:
      pthread_key_t key;
      pthread_key_create(&key, NULL);
      pthread_setspecific(key, (void*)value); // 设置线程特定数据
      void* data = pthread_getspecific(key);  // 获取数据
      

5 错误处理

  • thread
    • 异常驱动:如线程创建失败时抛出 std::system_error
    • 可以通过捕获异常并提取错误码来模拟“错误码”机制,避免异常传播对性能的潜在影响
      • 实现思路:通过 try-catch 块捕获线程创建时的异常,从异常对象中提取系统错误码,将其转换为整数或自定义错误码返回给调用者。
      #include 
      #include 
      #include 
      
      // 封装线程创建函数,返回错误码(0 表示成功)
      int create_thread_with_error_code(std::thread& thread, void(*func)(int), int arg) {
          try {
              thread = std::thread(func, arg); // 可能抛出 std::system_error
              return 0; // 成功
          } catch (const std::system_error& e) {
              // 提取错误码
              std::error_code ec = e.code();
              std::cerr << "Error: " << ec.message() << " (code: " << ec.value() << ")\n";
              return ec.value(); // 返回系统错误码
          }
      }
      
      // 示例线程函数
      void task(int arg) {
          // 线程逻辑
      }
      
      int main() {
          std::thread t;
          int err = create_thread_with_error_code(t, task, 42);
      
          if (err != 0) {
              // 处理错误(如资源不足、权限问题等)
              std::cerr << "Failed to create thread (error code: " << err << ")\n";
          } else {
              t.join(); // 等待线程结束
          }
      
          return 0;
      }
      
      • 分析
        • 性能

          • 异常仅在错误时抛出:若线程创建成功(常见场景),try 块的额外开销几乎可以忽略(现代编译器会优化 try 块的无异常路径)。
          • 错误处理局部化:仅在错误发生时捕获异常,避免了异常传播到上层调用栈的开销。
          • pthread 对比
            • pthread_create 直接返回错误码,无异常机制,理论上略高效。
            • 但实际差异通常微小,除非在超高频创建线程的场景(此类场景本身应避免)。
        • 错误码来源

          • std::system_error::code() 中提取的 std::error_code 对应底层系统错误:
            • Linux/macOS:通常映射到 errno(如 EAGAINEINVAL)。
            • Windows:映射到 GetLastError() 的错误码。
          • 可通过 ec.value() 获取原始错误码值,与系统文档对照处理。
        • 禁用异常的极端场景
          若编译时禁用 C++ 异常(如 -fno-exceptions),则 std::thread 的构造函数无法抛出异常,其行为是未定义的(通常直接终止程序)。此时需其他方案:

          • 使用 pthread 替代:直接调用 pthread_create(牺牲 C++ 类型安全和 RAII)。
          • 自定义线程包装器:结合 pthreadstd::thread 的 RAII 封装(复杂且易出错)。
        • 适用场景以及总结

          • 可以模拟错误码机制:通过捕获 std::system_error 并提取错误码,避免异常传播。
          • 性能影响可控:异常仅在错误时抛出,正常路径无额外开销。
          • 兼容性与灵活性:在需要底层控制或禁用异常时,可回退到 pthread

          通过合理封装,std::thread 既能享受 C++ 的现代特性,又能通过错误码机制满足对性能或传统错误处理风格的需求。

场景 推荐方案
常规 C++ 项目,需类型安全和简洁性 std::thread + 异常捕获转错误码
高频线程创建/销毁,要求极致性能 pthread 直接操作
禁用 C++ 异常 使用 pthread 或平台特定 API
  • pthread
    • 返回错误码:通过返回值判断错误(如 EAGAIN 表示资源不足)。

6 线程取消

  • thread
    • 不提供强制终止线程的机制,需通过共享变量或原子标志协作式退出:
      std::atomic<bool> exit_flag(false);
      std::thread t([&] { while (!exit_flag) { /* ... */ } });
      exit_flag = true; // 通知线程退出
      t.join();
      
  • pthread
    • 支持 pthread_cancel 强制终止线程,但需谨慎处理资源清理(通过 pthread_cleanup_push/pthread_cleanup_pop)。

7 可移植性

  • thread
    • 标准库实现,跨平台支持(Windows、Linux、macOS 等)。
  • pthread
    • 主要在类 Unix 系统(Linux、macOS)原生支持,Windows 需第三方库(如 pthreads-win32)。

8 其他

  • thread
    • c++11基础功能完备,但缺少高级特性(如线程优先级设置)。
    • C++17/20 扩展了并行算法和原子操作。
  • pthread
    • 支持更多底层控制(如线程调度策略、优先级、信号处理)。

9 对比

特性 C++11 std::thread POSIX pthread
设计理念 面向对象,类型安全,RAII 函数式,显式资源管理
可移植性 跨平台(标准 C++ 支持) 类 Unix 系统为主,Windows 需额外库
错误处理 异常机制 返回错误码
同步机制 RAII 封装(自动锁管理) 手动初始化/销毁
线程局部存储 thread_local 关键字 pthread_key_create 等函数
线程取消 协作式(通过标志位) 支持强制终止(pthread_cancel
适用场景 现代 C++ 项目,强调安全性与可维护性 需要底层控制或兼容旧代码
  • 优先 std::thread:若项目基于 C++11 或更新标准,且无需底层控制,推荐使用 std::thread,其代码更简洁、安全。
  • 选择 pthread:若需精细控制线程行为(如调度策略),或需兼容旧代码(尤其是跨平台 C 代码),则使用 pthread

你可能感兴趣的:(C/C++,c++,c语言)