0

0

Python timeoutcontextmanager 的自实现

舞夢輝影

舞夢輝影

发布时间:2026-02-14 19:35:02

|

618人浏览过

|

来源于php中文网

原创

signal.alarm无法实现通用timeout,因其仅主线程有效、不中断纯python计算、与多线程/异步冲突;可靠方案是threading+queue(兼容所有同步代码)或asyncio.wait_for(要求awaitable)。

python timeoutcontextmanager 的自实现

为什么不能直接用 signal.alarm 实现 timeoutcontextmanager

因为 signal.alarm 只在主线程生效,且会中断系统调用(比如 time.sleepsocket.recv),但对纯 Python 计算(如死循环、大列表推导)完全无效。更麻烦的是,它和多线程、异步代码根本冲突——一旦你在子线程里设 alarm,Python 会直接抛 ValueError: signal only works in main thread

所以自实现必须绕开信号机制,靠协程或线程协作式中断,或者依赖目标操作自身支持超时(比如 requests.get(timeout=...))。

  • 适用场景:需要包装一个「可能卡住但不响应信号」的同步阻塞调用,比如自定义网络协议解析、第三方库无 timeout 参数的函数
  • 不适用场景:想给 for i in range(10**9): pass 加超时——这种只能靠外部进程 kill 或改用 asyncio.wait_for + 拆成小步 yield
  • 关键取舍:用线程(兼容所有同步代码,但有 GIL 和资源开销)还是用 asyncio(轻量,但要求被包装函数是 awaitable)

用 threading.Thread + queue 实现最简可靠版

这是兼容性最强的方案:新开线程执行目标函数,主线程用 queue.Queue.get(timeout=...) 等结果。线程结束后自动释放,不用手动清理。

示例核心逻辑:

Anybot
Anybot

创建AI驱动的聊天机器人,快速、轻松地实现业务自动化

下载

立即学习Python免费学习笔记(深入)”;

import threading
import queue

def timeout_contextmanager(seconds):
    def decorator(func):
        def wrapper(*args, **kwargs):
            q = queue.Queue()
            def _target():
                try:
                    result = func(*args, **kwargs)
                    q.put(('success', result))
                except Exception as e:
                    q.put(('error', e))
            t = threading.Thread(target=_target, daemon=True)
            t.start()
            try:
                status, value = q.get(timeout=seconds)
                if status == 'success':
                    return value
                raise value
            except queue.Empty:
                raise TimeoutError(f'Function {func.__name__} timed out after {seconds}s')
        return wrapper
    return decorator
  • 必须设 daemon=True,否则超时时线程还在跑,程序无法退出
  • queue.get(timeout=...) 是唯一安全的等待方式;别用 t.join(seconds) —— 它不中断线程,只等结束,失去超时意义
  • 无法强制终止正在运行的线程(Python 没有 Thread.kill),所以超时后线程仍在后台执行,只是结果被丢弃。这对副作用操作(比如写文件、发请求)要格外小心

asyncio 版本要注意 event loop 生命周期

如果你的函数本身是 async 的,用 asyncio.wait_for 最干净。但注意:它只能在已有 event loop 的上下文中运行,不能在已关闭或未启动的 loop 里调用。

常见错误现象:RuntimeError: no running event loopRuntimeWarning: coroutine ... was never awaited

  • 正确姿势:确保调用方在 async 函数内,且 event loop 正在运行(比如用 asyncio.run(main()) 启动)
  • 不要在普通函数里调用 asyncio.run(...) 套一层——每次都会新建 loop,开销大且嵌套容易出错
  • 参数差异:asyncio.wait_for(coro, timeout=...) 的 timeout 是 float 秒,支持小数(如 0.1),而 threading 版通常只处理整数秒精度
  • 性能影响:asyncio 版无额外线程开销,但要求整个调用链路都是 async,改造成本可能高于加个线程

别忽略 timeout 对异常传播的影响

超时不是“取消”,而是“放弃等待”。原函数如果在超时后仍继续执行并抛异常,这个异常不会被捕获,也不会传给上层——它就在那个孤立线程里静默消失(除非你手动 log)。

  • 如果函数有副作用(如修改全局变量、写临时文件),超时后这些操作可能已完成、部分完成、或根本没开始,行为不可预测
  • 想真正“取消”任务,得函数自己支持 cancellation token(比如接受一个 stop_event: threading.Event 参数),timeout 只负责通知,不强制中断
  • 最容易被忽略的一点:测试 timeout 路径时,别只测“刚好超时”,一定要覆盖“提前返回”和“超时后原函数仍继续运行”两种情况,否则上线后可能数据不一致

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

587

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

104

2025.10.23

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6388

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

834

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1082

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1545

2024.03.01

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

85

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

101

2025.09.18

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

23

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.4万人学习

Django 教程
Django 教程

共28课时 | 4.3万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号