当前位置: 首页 > news >正文

Linux开发中的线程管理(C++11 std::thread)

前言

std::thread 是 C++11 引入的一个类,是 C++11 标准库中的一个关键特性,它提供了一种在 C++ 程序中创建和管理线程的方法。通过使用 std::thread,可以很容易地在你的 C++ 程序中创建多线程应用程序。每个 std::thread 对象都代表了一个独立的执行线程,这些线程可以并行地执行不同的任务。

1. std::thread线程的创建

std::thread();//默认构造函数template< class Function, class... Args >
explicit thread(Function&& f, Args&&... args);//可调用对象构造thread(thread&& other) noexcept;//移动构造函数thread(const thread&) = delete;//拷贝构造(删除)

std::thread是不可拷贝的,但可以移动。
该构造函数接收另一个std::thread对象,并接管其线程。

std::thread对象不能被拷贝,因为线程的所有权是唯一的。
这样设计是为了避免多个std::thread对象管理同一个线程,导致未定义行为。

#include <iostream>
#include <thread>void func(int x) {std::cout << "Thread function with arg: " << x << std::endl;
}int main() {std::thread thread;  // 默认构造,不与任何线程关联std::cout << "t is joinable? " << t.joinable() << std::endl; // 输出 0(false)std::thread thread_1(func, 10); // 创建线程并执行 func(10)thread_1.join();  // 等待线程执行完毕std::thread thread_2(func);std::thread thread_3 = std::move(thread_2); // t1移动到t2,t1不再管理线程thread_3.join();std::thread thread_4(func);// std::thread t2 = t1; // ❌ 错误,不能拷贝return 0;
}

一般创建子线程的流程

{// 创建client端的socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);// 连接服务器成功,启动接收子线程std::thread readTask(readTaskHandler, clientfd); // pthread_createreadTask.detach();                               // pthread_detach
}

2. std::thread线程的管理

等待线程结束
主线程可以通过调用join()方法来等待其他线程完成。当线程完成后则会自动释放申请的资源,进行收尸操作。
如果不调用join()detach(),则当 std::thread 对象被销毁时,程序会调用 std::terminate()终止未结束的线程,抛出一个异常。这可能会导致资源泄露或其他问题。
分离线程
默认情况下,新创建的线程会与创建它的线程(通常是主线程)同步。可以通过调用 detach() 方法来分离线程,这样主线程就可以继续执行,而不需要等待新线程结束。调用 .detach() 会使当前线程独立运行,从而使得该线程在后台运行,而不阻塞主线程或其他线程的执行。具体来说.detach() 的作用是将线程从其所属的 std::thread 对象中分离出来,这样 std::thread 对象就不再管理这个线程的生命周期了。
通过调用detach()方法,可以使线程独立于主线程运行。一旦线程被分离,就不能再次对其调用 join() detach(),并且当主线程结束时,分离的线程将继续运行,直到完成其任务。

#include <iostream>  
#include <thread>  void threadFunction() {  for (int i = 0; i < 5; ++i) {  std::cout << "Thread is running, count: " << i << std::endl;  std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作  }  
}  int main() {  std::thread thread_1(threadFunction); // 创建并启动线程  // 在这里可以做一些其他工作,但这里我们只是等待线程完成  thread_1.join(); // 等待线程t完成  std::cout << "Thread has finished execution." << std::endl;  std::thread thread_2(threadFunction);  thread_2.detach(); // 分离线程,主线程继续执行  // 注意:分离后,主线程结束时不会等待这个线程完成  // 这里的程序可能在后台线程完成之前就结束了  // 在实际应用中,你可能需要其他机制来确保所有线程都已完成return 0;  
}

std::thread的不安全操作

#include <iostream>  
#include <thread>  void threadFunction() {  // 假设这里有一些耗时的操作  
}  int main() {  {  std::thread t(threadFunction); // 创建一个线程,但作用域仅限于这个块  // 注意:我们没有调用 t.join() 或 t.detach()  // 当这个块结束时,t 的析构函数将被调用,但线程仍在运行  // 这将导致 std::terminate() 被调用,从而终止程序  }  // 这段代码不会被执行,因为程序已经在上面的块结束时终止了  std::cout << "This line will not be executed." << std::endl;  return 0; // 永远不会到达这里  
}

上述情况应该使用detach()分离出来,否则离开作用域后创建的子线程将直接进行析构

如果不调用 join() detach(),并且 std::thread 对象在作用域结束时被销毁,程序将调用 std::terminate() 终止。这是因为 C++ 标准要求,如果 std::thread 的析构函数被调用,并且线程仍然是可连接的(即,没有被 join() detach()),则必须调用 std::terminate()
为了避免这种情况,应该在 std::thread 对象被销毁之前调用 join()detach()。如果知道线程何时会结束,通常使用 join() 是更安全的选择,因为它可以确保主线程等待子线程完成。如果您不关心子线程的完成时间,或者想要让子线程独立于主线程运行,那么可以使用 detach()。但是,请注意,使用 detach() 时需要小心管理线程的生命周期和资源共享。

3. std::thread 的常用成员函数

成员函数功能概述注意/用途
joinable()检查线程对象是否可被 join。如果线程正在执行或可执行且未被 join 或 detach,则返回 true;否则返回 false。不带参构造的 std::thread 对象或已被移动的 std::thread 对象不可 join。
join()阻塞当前线程,直到被 join 的线程完成其执行。如果线程未启动或已被 join/detach,则行为未定义(通常抛出异常)。确保子线程在主线程继续执行之前完成其任务。
detach()将线程与 std::thread 对象分离,允许线程在后台继续运行。分离后,线程不再与任何 std::thread 对象关联,且不能被 join。一旦线程被 detach,就无法再通过 std::thread 对象控制或等待该线程。
get_id()获取线程的标识符(ID),类型为 std::thread::id。该 ID 在线程生命周期内唯一,但结束后可能会被重用。用于标识和区分不同的线程。
native_handle()获取与实现相关的本机线程句柄。具体行为和返回值取决于操作系统和 C++ 运行时库。使用时需了解当前平台的线程 API,并谨慎处理句柄以避免资源泄露或安全问题。
hardware_concurrency()返回硬件支持的并发线程数量的估计值(静态成员函数)。此值是一个提示,表示系统可能支持的并行线程数,但并非绝对限制。帮助开发者在创建线程时决定合适的线程数量。
swap()交换两个 std::thread 对象的状态。如果两个对象都表示活动的线程,则它们的执行不受影响;但如果一个对象是空的,则另一个对象将不再与任何线程关联。在需要转移线程所有权或管理线程集合时很有用。
http://www.xdnf.cn/news/217369.html

相关文章:

  • Pytorch 反向传播
  • 塔能照明节能服务流程:精准驱动工厂能耗优化
  • leetcode:3005. 最大频率元素计数(python3解法)
  • 第三次作业(密码学)
  • 【android bluetooth 协议分析 06】【l2cap详解 11】【l2cap连接超时处理逻辑介绍】
  • (29)VTK C++开发示例 ---绘制两条彩色线
  • 想做博闻强记的自己
  • IoTDB数据库建模与资源优化指南
  • Python中的defaultdict方法
  • 驱动开发硬核特训 · Day 24(下篇):深入理解 Linux 内核时钟子系统结构
  • 【深度学习的灵魂】图片布局生成模型LayoutPrompt(1)
  • MATLAB函数调用全解析:从入门到精通
  • 【Linux】g++安装教程
  • Linux 命名管道+日志
  • 婴幼儿托育实训室生活照料流程标准化设计
  • Flowable7.x学习笔记(十五)动态指定用户分配参数启动工作流程
  • AutogenStudio使用
  • 快速掌握向量数据库-Milvus探索2_集成Embedding模型
  • AI技术前沿:Function Calling、RAG与MCP的深度解析与应用实践
  • 基于PyTorch的图像分类特征提取与模型训练文档
  • 集群系统的五大核心挑战与困境解析
  • EtherCAT转CANopen方案落地:推动运动控制器与传感器通讯的工程化实践
  • CKESC Breeze 6S 40A_4S 50A FOC BEC电调测评:全新vfast 技术赋能高效精准控制
  • 低代码平台部署方案解析:百特搭四大部署方式
  • 大模型推理:Qwen3 32B vLLM Docker本地部署
  • 强化学习贝尔曼方程推导
  • 流量守门员:接口限流艺术
  • Manus AI多语言手写识别技术全解析:从模型架构到实战部署
  • JavaScript 中深拷贝浅拷贝的区别?如何实现一个深拷贝?
  • 信雅达 AI + 悦数 Graph RAG | 大模型知识管理平台在金融行业的实践