如何非阻塞获取Python多线程异步方法的执行返回结果
问题根因
串行阻塞和线程装饰器实现无关,是调用逻辑错误导致的:Future.result() 方法本身会阻塞当前线程,直到对应任务执行完成才返回结果。你写的print(test.run_something().result(), test.run_something().result()) 执行逻辑是:
- 执行第一个
test.run_something(),启动第一个工作线程,返回对应future对象 - 立刻调用第一个future的
.result(),主线程挂起等待5秒直到第一个任务执行完 - 第一个任务返回结果后,才会执行第二个
test.run_something()启动第二个工作线程 - 再挂起等待5秒拿到第二个任务结果
两个工作线程根本没有同时启动,自然会出现5秒的时间差,表现为串行阻塞。
可行实现方案
核心原则是先批量启动所有异步工作线程,持有所有返回的future对象,所有任务都启动完成后再等待获取结果,不要启动一个任务就立刻阻塞等待它的结果。
修正后的并行执行代码
import time import datetime from threading import Thread from concurrent.futures import Future def call_with_future(fn, future, args, kwargs): try: result = fn(*args, **kwargs) future.set_result(result) except Exception as exc: future.set_exception(exc) def threaded(fn): def wrapper(*args, **kwargs): future = Future() # 设为守护线程,避免服务退出时线程残留 Thread(target=call_with_future, args=(fn, future, args, kwargs), daemon=True).start() return future return wrapper class Test: @threaded def run_something(self): time.sleep(5) return datetime.datetime.now() test = Test() # 第一步:先启动所有任务,两个工作线程并行执行 task1 = test.run_something() task2 = test.run_something() # 第二步:所有任务启动完成后,再等待获取结果 print(task1.result(), task2.result())
运行后两个返回时间戳差值在毫秒级,证明两个任务并行执行,没有串行阻塞。
REST服务对接oracledb的生产注意事项
针对你的数据库查询服务场景,有几个实践要点:
- 不要用临时创建线程的简易装饰器,生产环境直接用
concurrent.futures.ThreadPoolExecutor维护固定大小的线程池,避免高并发下无限制创建线程耗尽服务资源。oracledb属于同步阻塞IO驱动,在线程池中执行可以完全避免阻塞主线程的请求处理逻辑。 - 如果你使用FastAPI、Sanic这类异步Web框架,不要在协程中直接调用同步的oracledb查询方法,用
asyncio.to_thread()或者框架自带的线程池依赖将查询任务调度到工作线程执行,主线程可以继续处理其他进来的请求。 - 对于耗时超过接口超时阈值的查询,建议改成异步任务模式:接口收到请求后先返回任务ID,查询任务在线程池中执行,结果写入缓存或数据库,前端通过轮询、WebSocket等方式获取最终结果,避免请求长时间挂住。
- oracledb的连接对象不能跨线程共享,每个工作线程执行查询时需要从独立的数据库连接池获取连接,使用完及时归还,避免出现连接状态异常。
注意:不要试图直接用async/await语法包装同步的oracledb调用实现协程异步,Python协程本身不会把阻塞IO自动转为非阻塞,同步调用只要跑在主事件循环线程里,就会阻塞所有其他请求的处理,必须调度到独立工作线程执行才能实现非阻塞效果。
内容的提问来源于stack exchange,提问作者Kyon
相关产品推荐
相关产品推荐

