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

【C/C++】线程状态以及转换

文章目录

  • 线程状态以及转换
    • 1 基本状态
      • 1.1 新建(New)
      • 1.2 就绪(Ready / Runnable)
      • 1.3 运行中(Running)
      • 1.4 阻塞/等待(Blocked / Waiting / Sleeping)
      • 1.5 挂起(Suspended)
      • 1.6 终止(Terminated / Dead / Exit)
      • 注意点
    • 2 状态转换
      • 2.1 C/C++ 线程状态转换图(基于 Linux/POSIX 和 `std::thread`)
      • 2.2 状态转换说明
      • 2.3 示例:多个状态的转换代码
    • 3 调试
      • 3.1 Linux 中线程/进程的典型状态码
      • 3.2 深入 `/proc/[pid]/task/` 查看线程状态
      • 3.3 状态之间的实际转换(C++ 映射)
      • 3.5 调试技巧
    • 总结:线程状态的决定者是谁?

线程状态以及转换

线程的基本状态通常用于描述线程在程序执行过程中的生命周期。


1 基本状态

1.1 新建(New)

  • 说明:线程对象已经创建,但尚未启动。
  • 示例(C++):std::thread t(myFunction);(如果尚未调用 join()detach()

1.2 就绪(Ready / Runnable)

  • 说明:线程已经准备好运行,但由于 CPU 正忙,暂时未被调度执行。
  • 特征:已具备运行条件,等待调度。

1.3 运行中(Running)

  • 说明:线程正在由 CPU 调度并执行任务。
  • 特征:只有一个线程能在某一时刻占用一个 CPU 核心运行。

1.4 阻塞/等待(Blocked / Waiting / Sleeping)

  • 说明:线程暂时无法继续运行,处于等待某些资源或事件状态。

  • 常见原因:

    • 等待锁(mutex)释放
    • 等待条件变量(std::condition_variable
    • 调用了 sleep_for() / sleep_until()
    • 等待 I/O 操作完成

1.5 挂起(Suspended)

  • 说明:线程被人为暂停,暂时不会被调度(某些系统中才存在,如 Windows)。
  • 补充:这不是所有系统都显式支持的状态。

1.6 终止(Terminated / Dead / Exit)

  • 说明:线程执行完任务或被强制终止,生命周期结束。

  • 注意事项:

    • 线程终止后不能被重启。
    • C++ 中,必须在适当时机调用 join()detach(),否则可能引发资源泄露。

注意点

  • std::thread 构造后即启动,不能“延迟启动”。
  • join():主线程等待子线程执行完毕。
  • detach():子线程后台运行,主线程不再关心其状态。
  • 未调用 join()detach() 就析构 std::thread,会导致程序崩溃。

2 状态转换

在 C/C++ 中,线程状态的转换并不像 Java 那样有统一的虚拟机控制模型,而是依赖于底层操作系统(如 Linux、Windows)调度机制。C++ 本身通过 std::thread 提供了对系统线程(如 POSIX Threads 或 Windows Threads)的封装,但不显式暴露线程状态。

结合操作系统线程模型,理解 C/C++ 中线程状态的转换路径。


2.1 C/C++ 线程状态转换图(基于 Linux/POSIX 和 std::thread

简单版本

 [New] ↓[Ready] ↔ [Running] → [Terminated]↑          ↓[Blocked] ←---
 +--------+       thread constructor|  New   | --------------------------++--------+                           ||                               v|                        +-------------+|        OS调度          |   Runnable  |+----------------------> +-------------+|             |v             |+--------------+      | 被抢占或 yield|   Running    | <----++--------------+|+----------------------+------------------------------+|                      |                              |v                      v                              v
[Waiting on lock]   [Waiting on cond_var]          [sleep_for / sleep_until]Blocked              Waiting (CondVar)             Sleeping+                      +                              +|                      |                              |+---------> 信号/条件满足/定时器超时  <---------------+|v+--------------+|   Runnable   |+--------------+|v+--------------+|   Running    |+--------------+|执行结束 or 异常|v+--------------+| Terminated   |+--------------+

Linux 实际版

            +-------------+|   Running   | <--------++-------------+          ||        ^            |preempt    schedule       |v        |            |+-------------+           ||   Runnable   | ---------++-------------+|+------+------+|             |+-------+     +--------+| Sleep |     |  Disk  ||  (S)  |     | Sleep  |+-------+     |  (D)   ||         +--------+|               |v               v+---------------------+|     Runnable        |+---------------------+(via wakeup or I/O completion)

2.2 状态转换说明

状态名称触发条件示例代码
New → Runnable创建线程对象 std::thread t(...)std::thread t(myFunc);
Runnable → Running被操作系统调度自动完成,无需显式操作
Running → Blocked获取互斥锁失败(如 mutex.lock()std::unique_lock<std::mutex> lock(m);
Running → Waiting调用 condition_variable.wait()cv.wait(lock);
Running → Sleeping调用 std::this_thread::sleep_for(...)std::this_thread::sleep_for(1s);
Blocked/Waiting/Sleeping → Runnable条件满足/超时/锁释放由 OS 处理,代码中不可见
Running → Terminated线程函数返回/异常函数结束或 return

2.3 示例:多个状态的转换代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);std::cout << "Worker: waiting...\n";// Running → Waiting(挂起等待条件变量)cv.wait(lock, [] { return ready; });std::cout << "Worker: resumed and working...\n";// Running → Sleepingstd::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Worker: finished.\n";// → Terminated
}int main() {std::thread t(worker);std::this_thread::sleep_for(std::chrono::seconds(2));{std::lock_guard<std::mutex> lock(mtx);ready = true;std::cout << "Main: notifying worker...\n";}cv.notify_one();t.join();
}

执行结果

Worker: waiting...
Main: notifying worker...
Worker: resumed and working...
Worker: finished.

3 调试

3.1 Linux 中线程/进程的典型状态码

运行命令如:

ps -o pid,tid,stat,comm -L -p <pid>

或者使用 top,会看到类似以下状态码:

例如输出:

  PID   TID STAT COMMAND
12345 12345 Ss   my_program
12345 12346 Sl   my_program
12345 12347 Rl   my_program
12345 12348 S    my_program
  • Ss:主线程,sleeping 且为 session leader。
  • Sl:sleeping + 多线程(L)。
  • Rl:runnable + 多线程(L)。
  • S:普通 sleeping 线程。
状态码含义说明
RRunning / Runnable正在运行,或在运行队列中等待调度
SSleeping (Interruptible)可中断的睡眠,等待事件或条件,如 sleep()read()
DUninterruptible sleep不可中断的睡眠(通常为 IO),如等待磁盘或网络
TTraced / Stopped被调试器暂停或收到 SIGSTOP 信号
ZZombie僵尸进程,子进程已终止但父进程未调用 wait()
XDead非正常终止(很少见)

3.2 深入 /proc/[pid]/task/ 查看线程状态

Linux 把每个线程当作一个任务(task),在 /proc/[pid]/task/ 下有所有线程的子目录:

ls /proc/<pid>/task/

然后可以查看每个线程状态:

cat /proc/<pid>/task/<tid>/status

输出中有一行:

State:	S (sleeping)

其他可能值包括:

  • R (running)
  • D (disk sleep)
  • T (stopped)
  • Z (zombie)

3.3 状态之间的实际转换(C++ 映射)

Linux 状态对应 C++ 场景
R (Runnable)正在执行或准备被调度,CPU 调度队列中
S (Sleeping)std::this_thread::sleep_for、等待条件变量、I/O 等
D (Disk Sleep)被阻塞在磁盘或网络 I/O(不可中断)
T (Stopped)被调试或 SIGSTOP 暂停
Z (Zombie)线程/进程退出但未被 join()/wait()

3.5 调试技巧

  1. htop 可视化线程状态
htop
# F2 -> Display options -> Show custom thread names (如你设置了)
  1. gdb 附加调试线程状态
gdb -p <pid>
info threads
  1. perf top 查看哪些线程/函数在消耗 CPU

总结:线程状态的决定者是谁?

决定因素状态
程序员代码睡眠、等待、join 等显式操作
操作系统调度器Running / Runnable 切换、抢占等
锁竞争Blocked(mutex、spinlock)
http://www.xdnf.cn/news/623953.html

相关文章:

  • RabbitMQ 概述与安装
  • Spring AI 之结构化输出转换器
  • Python实现对大批量Word文档进行自动添加页码(16)
  • 使用腾讯云3台轻量云服务器快速部署K8s集群实战
  • 如何制作可以本地联网搜索的MCP,并让本地Qwen3大模型调用搜索回答用户问题?
  • Appium+python自动化(五)- 模拟器
  • axios报错: Uncaught ReferenceError: axios is not defined
  • Qt基础:数据容器类
  • 算法中的数学:费马小定理
  • redis配置带验证的主从复制
  • Windows 中动态库.dll 的 .lib 文件有什么作用?
  • x64_ubuntu22.04.5安装:cuda driver + cuda toolkit
  • 【Linux手册】Linux权限:系统世界的“门禁卡”
  • SOC-ESP32S3部分:10-GPIO中断按键中断实现
  • MySQL快速入门篇---联合查询
  • Vanna.AI:用检索增强技术革新SQL查询生成
  • Spark 中,map和foreach的区别
  • Spark on YARN 的运行架构总览
  • 构建跨平台C/C++项目的基石:现代构建套件设计指南
  • Python包__init__.py标识文件解析
  • 操作系统的内核态和用户态场景
  • 最小均方误差(MMSE)滤波器及其改进版
  • skywalking 10.2 源码编译
  • Kafka Streams 和 Apache Flink 的无状态流处理与有状态流处理
  • 伴随矩阵 -- 代数余子式矩阵的转置
  • 【PostgreSQL】数据探查工具1.0研发可行性方案
  • 数据结构与算法——链式二叉树
  • 讲述我的PLC自学之路 第九章
  • P2089 烤鸡
  • 【Elasticsearch入门到落地】13、DSL查询详解:分类、语法与实战场景