3. 线程生命周期

⏳ 线程生命周期

第3章 · 启动、运行、阻塞与结束 —— 掌控线程的每一帧

线程从被创建到终止,经历一系列明确的状态转换。理解这些状态是编写健壮多线程程序的基础,尤其在摄像头流水线中,线程的阻塞与唤醒直接影响帧率。

新建 就绪 运行 阻塞 终止 start() 调度 yield / 时间片 I/O / 锁 条件满足 / 唤醒 run() 结束

▲ 线程五大状态及转换关系(阻塞以琥珀色高亮)

1. 线程的启动

通过 threading.Thread(target=func) 创建线程对象后,线程处于新建状态。调用 start() 使其进入就绪队列,等待操作系统调度。

import threading
import time

def camera_task():
    # 模拟摄像头初始化
    time.sleep(0.1)
    print("摄像头线程已启动")

cap_thread = threading.Thread(target=camera_task, name="CameraThread")
cap_thread.start()   # 启动线程,进入就绪态

2. 运行与阻塞

线程获得 CPU 时间片后进入运行状态。当遇到 I/O 操作(如 cv2.VideoCapture.read() 等待帧)、time.sleep() 或请求锁时,线程主动阻塞,让出 CPU。阻塞结束后重新回到就绪队列。

运行 阻塞 (等待帧) 就绪 read() 阻塞 帧就绪 t1 t2 (阻塞) t3

▲ 摄像头读取线程在阻塞-就绪间切换,等待帧到达

3. 阻塞的常见原因

阻塞类型典型场景唤醒方式
I/O 阻塞摄像头 read()、网络请求数据就绪 / 超时
锁阻塞acquire() 未获得锁锁被释放
睡眠阻塞time.sleep()睡眠结束
条件变量wait() 等待事件notify() / notify_all()

4. 线程的结束

线程函数自然返回或抛出异常时,线程进入终止状态。也可以使用 threading.Event 优雅通知线程退出,避免强制终止。

import threading
import time

stop_event = threading.Event()

def camera_loop():
    while not stop_event.is_set():
        # 模拟读取帧
        time.sleep(0.03)
        print("帧处理中...")
    print("线程安全退出")

t = threading.Thread(target=camera_loop)
t.start()
time.sleep(0.2)
stop_event.set()   # 通知线程结束
t.join()           # 等待线程终止
运行 检查事件 终止 event.set() 退出循环

▲ 使用 Event 实现协作式终止,避免 Thread.stop()

5. 生命周期在摄像头流水线中的意义

  • 启动:为每一路摄像头创建独立线程,避免阻塞主循环。
  • 阻塞:合理利用阻塞等待帧,降低 CPU 空转;但需注意死锁。
  • 结束:使用 join() 确保资源释放,防止僵尸线程。