signal.alarm最轻量但仅限unix系统,依赖信号中断io阻塞调用,对纯cpu计算无效;需主线程使用、每次重设handler、不可嵌套。

用 signal.alarm 控制函数超时(仅 Linux/macOS)
这个方案最轻量,但只在类 Unix 系统有效,Windows 直接抛 AttributeError: module 'signal' has no attribute 'alarm'。它靠操作系统信号中断阻塞调用,对纯 CPU 计算无效——比如一个死循环不会被中断,只有系统调用(如 time.sleep、socket.recv)才会响应。
实操建议:
- 适合 IO 密集型任务,比如 HTTP 请求、文件读写、数据库查询
- 必须在主线程中使用;多线程里调用会报错或行为未定义
- 注册的
signal.signal(signal.SIGALRM, handler)会被后续 alarm 覆盖,记得每次用前重设 handler - 不能嵌套使用:两次
alarm(1)后再alarm(2),第一个定时器就失效了
import signal
<p>def timeout_handler(signum, frame):
raise TimeoutError("Operation timed out")</p><p>signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(3) # 3 秒后发 SIGALRM
try:
result = some_io_operation() # 如 requests.get()
finally:
signal.alarm(0) # 关闭定时器
用 threading.Timer + 线程协作中断
这是跨平台方案,原理是起一个子线程执行目标函数,主线程用 Timer 控制超时并尝试终止子线程。但 Python 没有安全强制杀线程的机制,threading.Thread.terminate() 根本不存在——你只能靠共享变量“礼貌请求”退出。
常见错误现象:Timer 到时了,但子线程还在跑,主程序已返回,变成后台幽灵线程;或者目标函数没检查退出标志,完全无视超时。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 目标函数必须主动轮询一个
stop_event或全局 flag,不能依赖外部中断 - 避免在子线程里做不可中断的 C 扩展调用(如某些 NumPy 运算),它们会卡住整个 GIL
- 用
thread.join(timeout=...)等待子线程退出,但 join 本身不中断线程,只是等待 - 超时后别忘了清理资源:关闭 socket、释放锁、取消 pending future
用 concurrent.futures.wait 配合 ThreadPoolExecutor
这是最推荐的通用做法,尤其适合已有异步思维或需要批量控制多个任务的场景。它不试图杀线程,而是让 executor 管理生命周期,用 wait(..., timeout=...) 判断是否完成,超时后直接丢弃 Future 对象。
注意点:
-
Future.cancel()在任务已开始执行时通常返回False,不代表线程被终止,只是标记“不想让它继续” - 线程池大小影响实际并发数,设太小会导致任务排队,表观超时时间变长
- 如果目标函数内部有长时间阻塞(如
time.sleep(10)),cancel 不生效,得靠函数自己支持中断逻辑 - Windows 下首次导入 multiprocessing 可能触发 spawn 开销,冷启动略慢
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED <p>with ThreadPoolExecutor() as executor: future = executor.submit(some_heavy_function) done, not_done = wait([future], timeout=5) if future in done: result = future.result() else:</p><h1>超时,future 仍可能在运行,但不再等待</h1><pre class="brush:php;toolbar:false;"><code> pass
asyncio 里用 asyncio.wait_for 的边界情况
如果你的代码已经是 async/await 风格,asyncio.wait_for 是最自然的选择。但它只对真正 awaitable 的对象生效,对同步阻塞调用(比如没包装的 time.sleep(5) 或 requests.get)完全无效——事件循环会被卡住,超时根本不起作用。
实操建议:
- 同步 IO 必须用
loop.run_in_executor包一层,否则等于白加 timeout - 不要在
wait_for内部混用async with和未超时处理的资源管理,容易漏 close - timeout 触发时抛
asyncio.TimeoutError,不是TimeoutError,捕获时别写错类型 - Python 3.11+ 支持
asyncio.timeout()上下文管理器,语义更清晰,但老版本得坚持用wait_for
超时不是开关,是协商机制;所有方案都依赖被控代码配合退出,硬中断在 Python 里基本不存在。最容易被忽略的是:你以为 cancel 了 future,其实底层线程还在跑,内存和句柄可能悄悄泄漏。







