前端 Python 3.12

threading

标准库线程:Thread、锁与条件变量、信号量、事件、定时器与栅栏;GIL 与 I/O 并发;链回并发导读与官方 threading 手册。

threading 在底层 _thread 之上提供 高层线程 APIThread、同步原语等)。需要「把任务丢给后台线程、按需取结果」时,可配合 concurrent.futures.ThreadPoolExecutor;线程间传数据常用 queue;任务级并发而不开多 OS 线程可看 asyncio

适用性:在 wasm32-emscripten / wasm32-wasi不可用(见 WebAssembly 平台)。

何时用线程

场景建议
多个 I/O 密集型 任务并行(网络、磁盘、等待)多线程仍合适
多个 CPU 密集型 Python 计算占满多核GIL 限制,同一时刻通常只有一个线程执行 Python 字节码;优先 multiprocessingProcessPoolExecutor
需要结构化任务并发、少碰锁ThreadPoolExecutorasyncio

CPython 下 GIL 意味着多线程不等于多核并行算力;与 内置类型线程安全 章节互补——业务共享可变状态仍须自行同步。

GIL(Global Interpreter Lock,全局解释器锁)是 CPython 解释器的实现细节。GIL 限制了同一时刻只有一个线程执行 Python 字节码,这意味着即使在多核 CPU 上,CPU 密集型 Python 原生代码依然难以通过多线程实现“多核并行提升计算性能”。 注意:某些 C 扩展或内建库(如大量计算的 numpy/scipy 等)可在 C 层释放 GIL,实现并行;但绝大多数 Python 层逻辑会持续受 GIL 限制。

直接运行的 Python 脚本默认是单进程、单线程的

模块成员总览

模块级函数与常量

名称返回值 / 类型说明
active_count()int当前存活 Thread 数量
current_thread()Thread当前控制线程;非 threading 创建则为功能受限的虚拟线程
enumerate()list[Thread]所有存活线程(含守护线程;不含已结束、未启动的)
main_thread()Thread主线程对象
get_ident()int当前线程标识符(可作 magic cookie)
get_native_id()intOS 分配的原生线程 ID(平台相关)
excepthook(args)处理 Thread.run() 未捕获异常;默认忽略 SystemExit
settrace / gettracethreading 启动的线程设置/获取追踪函数
settrace_all_threads3.12+:含已在运行的 Python 线程
setprofile / getprofile性能分析钩子(同上,有 setprofile_all_threads
stack_size([size])int新建线程栈大小(平台限制,见文档)
TIMEOUT_MAXfloatacquire / waittimeout 参数上限

类型与同步原语

名称说明
Thread在独立控制线程中运行活动
local线程本地存储
Lock不可重入的互斥锁(工厂,返回平台最优实现)
RLock可重入锁,同一线程可多次 acquire
Condition条件变量,配合锁与 wait / notify
Semaphore计数信号量
BoundedSemaphore有界信号量,release 不得超过初始值
Event线程间简单事件标志
Timer延迟执行的 Thread 子类
Barrier固定数量线程相互等待(3.2+)
BrokenBarrierError栅栏破损或重置时仍有线程等待

创建与等待线程

import threading

def worker(n):
    print(f"thread {n}")

t = threading.Thread(target=worker, args=(1,), name="worker-1")
t.start()   # 必须在独立线程中调用 run();同对象只能 start 一次
t.join()    # 阻塞直到线程结束。若设置 timeout,则等待指定秒数后返回,不会打断线程;
Thread 构造参数含义
targetrun() 调用的可调用对象
args / kwargs传给 target 的位置/关键字参数
name线程名;默认 Thread-N 或带 target.__name__
daemonTrue 为守护线程;默认继承创建者;须在 start() 之前 设置
方法 / 属性说明
start()启动线程;重复调用 RuntimeError
run()线程体;子类可重载,或默认调用 target(*args, **kwargs)
join(timeout=None)等待结束;超时后须用 is_alive() 判断是否仍存活;不能 join 当前线程或未 start 的线程
is_alive()run() 已开始且未结束
daemon全部为守护线程时解释器才退出;守护线程可能 ** abruptly 结束**,资源未必释放
ident / native_id线程标识;未 start 时为 None

未捕获异常由 threading.excepthook 处理(默认打印到 stderrSystemExit 静默)。

线程本地数据

import threading

mydata = threading.local()
mydata.x = 1  # 每个线程各自一份属性

threading.local 实例在不同线程中 互不影响;适合存放「每线程一份」的上下文(连接、请求 ID 等)。

锁:LockRLock

类型特点
Lock任意线程可 release;不可重入,同线程二次 acquire 会死锁
RLock持有线程release;可嵌套 acquire,须成对 release
lock = threading.Lock()
with lock:          # 推荐:上下文管理器
    # 临界区
    pass

# acquire(blocking=True, timeout=-1) -> bool
# release() 在未锁定锁上调用 -> RuntimeError
  • timeout(3.2+):秒,浮点;-1 表示无限等待;与 blocking=False 不能同时指定正 timeout。
  • 阻塞在 acquire 上的多个线程,哪一个release 后继续 未定义

条件变量 Condition

绑定(默认新建 RLock)。典型 生产者-消费者

import threading

cv = threading.Condition()
# 消费
with cv:
    cv.wait_for(an_item_is_available)  # 3.2+:循环 wait 直到 predicate 为真
    get_an_available_item()

# 生产
with cv:
    make_an_item_available()
    cv.notify()      # 唤醒至多 n 个等待者(默认 1)
    # cv.notify_all()
方法说明
wait(timeout=None)释放锁并阻塞,直到 notify / notify_all 或超时;返回是否因通知而非超时
wait_for(predicate, timeout=None)自动循环 wait 直到 predicate() 为真
notify(n=1) / notify_all()不释放锁;被唤醒线程须等调用方退出 with 后才能重新拿到锁

必须用 whilewait_for 检查条件:wait 可能 虚假唤醒 或条件已变。

信号量与事件

信号量 Semaphore(value=1) / BoundedSemaphore(value=1):内部计数,acquire 减、release 增;为 0 时 acquire 阻塞。保护 有限资源池(如 DB 连接数)时常用有界信号量,防止 release 次数多于 acquire

from threading import BoundedSemaphore

pool_sema = BoundedSemaphore(5)
with pool_sema:
    conn = connectdb()
    try:
        ...
    finally:
        conn.close()

事件 Event:内部布尔标志;set() / clear() / wait(timeout) / is_set()。一个线程 set(),其余 wait() 直到为真。

定时器与栅栏

from threading import Timer, Barrier

t = Timer(30.0, hello)  # 30 秒后在线程中调用 hello()
t.start()
t.cancel()  # 仅动作开始前有效

b = Barrier(2, timeout=5)  # 2 个线程都 wait() 后同时继续
b.wait()  # 返回 0 .. parties-1,可让一个线程做「代表」工作
类型要点
TimerThread 子类;间隔可能与指定值略有偏差
Barrierreset() / abort() 会使等待者收到 BrokenBarrierError;破损后宜新建

with 与同步原语

凡提供 acquire / releaseLockRLockConditionSemaphoreBoundedSemaphore 均支持 上下文管理器

with some_lock:
    ...
# 等价于 acquire + try/finally release

相关模块速查

模块关系
_thread底层 start_new_thread、最低级锁
queue.Queue线程安全的 FIFO,常用于工作队列
concurrent.futures线程池、Future 结果
multiprocessing多进程绕开 GIL 做 CPU 并行

易错点(排错速记)

  • 把多线程当多核加速纯 Python 计算:GIL 下 CPU 密集仍宜多进程或原生扩展。
  • 守护线程 + 未 join:主程序退出时守护线程被强杀,文件/连接可能未关闭。
  • Condition 不用循环检查条件:竞态与虚假唤醒会导致逻辑错误。
  • notify 后以为对方已继续:对方要等锁释放;生产者在 with 块内 notify 后仍占锁直到块结束。
  • Lock 同线程重入:用 RLockRLockrelease 次数必须等于 acquire
  • asyncio 事件循环里 time.sleep / 阻塞锁:会卡住循环;异步代码用 asyncio 原语。
  • WebAssembly:本模块不可用;浏览器里跑 Python 勿假设有 threading

资料:threading 文档

On this page