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

发布于:2025-05-25 ⋅ 阅读:(23) ⋅ 点赞:(0)

线程状态以及转换

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


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 → Sleeping
    std::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 线程。
状态码 含义 说明
R Running / Runnable 正在运行,或在运行队列中等待调度
S Sleeping (Interruptible) 可中断的睡眠,等待事件或条件,如 sleep()read()
D Uninterruptible sleep 不可中断的睡眠(通常为 IO),如等待磁盘或网络
T Traced / Stopped 被调试器暂停或收到 SIGSTOP 信号
Z Zombie 僵尸进程,子进程已终止但父进程未调用 wait()
X Dead 非正常终止(很少见)

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)

网站公告

今日签到

点亮在社区的每一天
去签到