python异步asyncio函数多线程操作run_in_executor和run_coroutine_threadsafe解析

刚接触异步asyncio,发现python文档一个版本一个变化,恼火。
东拼西凑终于解决了asyncio的多线程操作,记录下来。对于一个门外汉。太难了。

首先python3.9版本终于封装了一个多线程函数

asyncio.to_thread(func, /, *args, **kwargs) #函数直接开线程,传递参数。感觉这才像python

对于3.9版本以下,asyncio多线程实现就是,两个函数:

run_in_executor 和 run_coroutine_threadsafe

对应两种需求。
一个是主线程,直接新开线程,运行阻塞函数。方式就像之前写的同步代码一样。代码如下(用到了run_in_executor)

from concurrent.futures import ThreadPoolExecutor
import asyncio,time

def zusehanshu():   #测试的阻塞函数
           time.sleep(10)
           threadmingzi(
<think>我们正在解决用户的问题:Python asyncio RuntimeError: no running event loop 的解决方法。 根据引用内容,我们知道这个错误通常发生在没有运行的事件循环时尝试使用异步功能。下面我们将逐步解释错误原因并提供解决方案。 错误原因: 在异步编程中,事件循环(Event Loop)是核心,负责调度执行异步任务。当我们在没有启动事件循环的环境中直接调用异步函数或使用需要事件循环的异步操作时,就会抛出`RuntimeError: no running event loop`错误。 常见场景: 1. 在非异步函数中直接调用异步函数(例如在普通函数中直接`await`)。 2. 在未启动事件循环的线程中执行异步代码。 3. 在已经关闭的事件循环中尝试运行异步任务。 解决方案: 根据引用[1][2][3][4]中的信息,我们可以采用以下方法: 方法1:使用`asyncio.run()`来运行顶层入口点(适用于Python 3.7+) 在程序的主入口,使用`asyncio.run(main())`来运行异步函数。注意:`asyncio.run()`每次调用都会创建一个新的事件循环,并在结束时关闭它。因此,不要在已经运行事件循环的代码中调用`asyncio.run()`(这会导致另一个错误,如引用[4]所述)。 示例: ```python import asyncio async def main(): # 你的异步代码 pass if __name__ == '__main__': asyncio.run(main()) ``` 方法2:手动管理事件循环 如果你需要在同一个线程中多次运行异步代码,或者需要更精细的控制,可以手动管理事件循环。 示例: ```python import asyncio async def my_coroutine(): print("Hello") loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 设置为当前线程的事件循环 try: loop.run_until_complete(my_coroutine()) finally: loop.close() ``` 方法3:在已经运行事件循环的环境中获取当前事件循环 如果你确定当前线程已经有一个正在运行的事件循环(例如在异步函数内部),可以使用`asyncio.get_running_loop()`来获取当前事件循环(Python 3.7+)。注意,在异步函数内部,应该使用`get_running_loop`而不是`get_event_loop`,因为后者可能在没有事件循环时抛出异常。 示例(在异步函数内部): ```python import asyncio async def my_async_function(): loop = asyncio.get_running_loop() # 获取当前正在运行的事件循环 # 使用loop进行异步操作 ``` 方法4:使用`asyncio.get_event_loop()`的注意事项 在Python 3.10中,`asyncio.get_event_loop()`在没有事件循环时会抛出`RuntimeError`。因此,在不确定是否有事件循环时,可以使用以下方式: ```python try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) ``` 方法5:在Jupyter Notebook等环境中使用 在Jupyter Notebook中,通常已经有一个正在运行的事件循环。此时,可以使用`nest_asyncio`包(如引用[2]所述)来允许在运行的事件循环中再次运行事件循环。 安装`nest_asyncio`: ```bash pip install nest_asyncio ``` 在代码中应用: ```python import nest_asyncio nest_asyncio.apply() # 然后就可以在已经运行的事件循环中运行异步代码了 ``` 注意事项: - 在异步编程中,确保你的异步调用在事件循环运行的环境中执行。 - 避免在同步代码中直接调用异步函数(除非使用`asyncio.run`或手动启动事件循环)。 - 在编写库代码时,尽量让用户传入事件循环,或者使用`asyncio.get_running_loop()`(在异步上下文中)。 通过以上方法,可以解决`RuntimeError: no running event loop`错误。 如果问题仍然存在,请检查代码结构,确保异步操作在正确的事件循环上下文中执行。 生成相关问题: 1. 如何理解Python中的事件循环(Event Loop)? 2. 在异步编程中,如何管理多个异步任务? 3. 使用`asyncio`时,如何避免事件循环的重复创建关闭? 4. 在Jupyter Notebook中使用异步编程有什么特殊注意事项? 5. 如何调试异步代码中的常见错误?</think>### Python asyncio RuntimeError: no running event loop 解决方法 #### 错误原因分析 `RuntimeError: no running event loop` 错误发生在以下场景: 1. **未启动事件循环时调用异步操作**:在未创建或启动事件循环的上下文中直接调用异步函数 2. **跨线程问题**:在非主线程中未正确设置事件循环 3. **事件循环提前关闭**:手动关闭事件循环后仍尝试执行异步任务 4. **错误调用位置**:在异步函数外部直接使用 `asyncio.get_running_loop()` 而非 `asyncio.get_event_loop()` #### 解决方案 ##### 方法1:显式创建并运行事件循环(推荐) ```python import asyncio async def my_task(): print("执行异步任务") # 正确调用方式 loop = asyncio.new_event_loop() # 创建新事件循环 asyncio.set_event_loop(loop) # 设置为当前线程的事件循环 try: loop.run_until_complete(my_task()) # 运行异步任务 finally: loop.close() # 显式关闭循环 ``` ##### 方法2:使用异步入口点(Python 3.7+) ```python import asyncio async def main(): # 在此处调用异步函数 await my_task() if __name__ == "__main__": asyncio.run(main()) # 自动管理事件循环生命周期 ``` ##### 方法3:在异步上下文中获取当前循环 ```python import asyncio async def proper_usage(): # 正确:在异步函数内获取运行中的循环 loop = asyncio.get_running_loop() await loop.run_in_executor(None, blocking_func) # 错误示例(会导致报错): # loop = asyncio.get_running_loop() # 在非异步上下文调用 ``` ##### 方法4:跨线程解决方案 ```python import asyncio from threading import Thread def worker(): # 每个线程需要独立事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(async_task()) Thread(target=worker).start() ``` ##### 方法5:检查事件循环状态 ```python import asyncio def safe_async_call(): try: loop = asyncio.get_running_loop() except RuntimeError: # 无运行中的循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) return loop ``` #### 最佳实践建议 1. **入口点统一**:程序入口始终使用 `asyncio.run(main())` 2. **避免混合同步/异步**:不要在同步函数中直接调用 `await` 3. **线程隔离**:每个线程维护独立事件循环 4. **资源清理**:使用 `try/finally` 确保循环关闭 5. **版本兼容**:Python < 3.7 需使用显式循环管理 > 关键点:`asyncio.get_running_loop()` 只能在已启动事件循环的上下文中调用,而 `asyncio.get_event_loop()` 在无循环时会自动创建(但需后续手动运行)[^3][^4]。 --- #### 相关问题 1. `asyncio.run()` 手动事件循环管理的主要区别是什么? 2. 如何在多线程应用中安全使用 asyncio 事件循环? 3. Python 异步编程中 `get_event_loop()` `get_running_loop()` 有何本质区别? 4. 为什么在 Jupyter Notebook 中运行异步代码需要特殊处理? 5. 异步任务未正常关闭可能导致哪些资源泄漏问题?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值