threading
标准库线程:Thread、锁与条件变量、信号量、事件、定时器与栅栏;GIL 与 I/O 并发;链回并发导读与官方 threading 手册。
threading 在底层 _thread 之上提供 高层线程 API(Thread、同步原语等)。需要「把任务丢给后台线程、按需取结果」时,可配合 concurrent.futures.ThreadPoolExecutor;线程间传数据常用 queue;任务级并发而不开多 OS 线程可看 asyncio。
适用性:在 wasm32-emscripten / wasm32-wasi 上 不可用(见 WebAssembly 平台)。
何时用线程
| 场景 | 建议 |
|---|---|
| 多个 I/O 密集型 任务并行(网络、磁盘、等待) | 多线程仍合适 |
| 多个 CPU 密集型 Python 计算占满多核 | 受 GIL 限制,同一时刻通常只有一个线程执行 Python 字节码;优先 multiprocessing 或 ProcessPoolExecutor |
| 需要结构化任务并发、少碰锁 | ThreadPoolExecutor 或 asyncio |
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() | int | OS 分配的原生线程 ID(平台相关) |
excepthook(args) | — | 处理 Thread.run() 未捕获异常;默认忽略 SystemExit |
settrace / gettrace | — | 为 threading 启动的线程设置/获取追踪函数 |
settrace_all_threads | — | 3.12+:含已在运行的 Python 线程 |
setprofile / getprofile | — | 性能分析钩子(同上,有 setprofile_all_threads) |
stack_size([size]) | int | 新建线程栈大小(平台限制,见文档) |
TIMEOUT_MAX | float | acquire / wait 等 timeout 参数上限 |
类型与同步原语
| 名称 | 说明 |
|---|---|
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 构造参数 | 含义 |
|---|---|
target | run() 调用的可调用对象 |
args / kwargs | 传给 target 的位置/关键字参数 |
name | 线程名;默认 Thread-N 或带 target.__name__ |
daemon | True 为守护线程;默认继承创建者;须在 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 处理(默认打印到 stderr,SystemExit 静默)。
线程本地数据
import threading
mydata = threading.local()
mydata.x = 1 # 每个线程各自一份属性threading.local 实例在不同线程中 互不影响;适合存放「每线程一份」的上下文(连接、请求 ID 等)。
锁:Lock 与 RLock
| 类型 | 特点 |
|---|---|
Lock | 任意线程可 release;不可重入,同线程二次 acquire 会死锁 |
RLock | 仅 持有线程 可 release;可嵌套 acquire,须成对 release |
lock = threading.Lock()
with lock: # 推荐:上下文管理器
# 临界区
pass
# acquire(blocking=True, timeout=-1) -> bool
# release() 在未锁定锁上调用 -> RuntimeErrortimeout(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 后才能重新拿到锁 |
必须用 while 或 wait_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,可让一个线程做「代表」工作| 类型 | 要点 |
|---|---|
Timer | Thread 子类;间隔可能与指定值略有偏差 |
Barrier | reset() / abort() 会使等待者收到 BrokenBarrierError;破损后宜新建 |
with 与同步原语
凡提供 acquire / release 的 Lock、RLock、Condition、Semaphore、BoundedSemaphore 均支持 上下文管理器:
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同线程重入:用RLock;RLock的release次数必须等于acquire。- 在
asyncio事件循环里time.sleep/ 阻塞锁:会卡住循环;异步代码用asyncio原语。 - WebAssembly:本模块不可用;浏览器里跑 Python 勿假设有
threading。
资料:threading 文档