asyncio动态添加协程 关闭协程

本文介绍了如何在Python的asyncio库中动态添加协程并关闭另一个线程的事件循环。通过创建线程并在其中运行新的事件循环,可以利用`run_coroutine_threadsafe`来安全地执行协程。同时,文章讨论了每个线程默认的select循环以及如何管理这些循环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实简单的不得了.  弄个线程,在线程里运行一个新的事件循环. 然后调用 run_coroutine_threadsafe 即可. 

asyncio基础 : python 异步 asyncio

asyncio.get_event_loop() 默认是主线程的事件循环 

每个线程一个事件循环 , 默认情况select循环

下面给出实现:

mport asyncio

#线程
def thread_running_loop(lp:asyncio.AbstractEventLoop):
    print('loop running')
    asyncio.set_event_loop(lp)    #在此线程中设置一个新的事件循环,默认情况下事件循环是主线程的
    lp.run_forever()              #一直运行


#随意的一个协程
async def xxx(arg):
    print('ready to work arg:' , arg)
    await  asyncio.sleep(1)
    print('done ', arg)



#创建一个新的事件循环给子线程
newlp = asyncio.new_event_loop()
t = threading.Thread(target=thread_running_loop,args=(newlp,))
t.start()


#添加5个协程. 并指定事件循环 (第2个参数)
for i in range(5):
    asyncio.run_coroutine_threadsafe(xxx(i),newlp)

t.join()

 

关闭 另一个线程的事件循环:


                
<think>我们正在讨论Python的asyncio模块,特别是关于在asyncio.run中主协程等待子协程完成的问题。根据引用,我们知道asyncio.run是用于运行一个协程并等待其完成的,它通常作为程序的入口点。然而,用户的问题是关于主协程(即被asyncio.run运行的协程)如何等待它内部创建的子协程完成。 根据引用[3]和[4],我们可以知道,在异步编程中,当我们使用`await`关键字来调用一个协程时,当前协程会挂起直到被调用的协程完成。但是,如果我们想要同时运行多个协程(即并发执行),我们不应该使用`await`一个一个地等待,因为那样会顺序执行。相反,我们应该使用`asyncio.create_task`来将协程包装成任务,然后使用`await`来等待这些任务完成。 在用户的问题中,他提到“主协程需要等待新协程完成”,这实际上是asyncio的默认行为:如果你使用`await`来等待一个协程,那么当前协程会等待直到那个协程完成。但是,如果主协程创建了多个任务,并且希望等待所有这些任务完成(即使它们是并发的),我们可以使用`asyncio.gather`或`asyncio.wait`。 然而,用户可能遇到了这样的问题:在`asyncio.run(main())`中,`main`协程内部创建了一些子协程(任务),但主协程(main)在退出时,如果还有未完成的任务,那么这些任务会被取消。因此,我们需要确保主协程等待所有子任务完成后再退出。 解决方法: 1. 使用`asyncio.create_task`创建任务,这样它们会在事件循环中并发运行。 2. 在主协程中,使用`await`来等待所有任务完成。可以使用`asyncio.gather`来收集多个任务并等待它们完成。 例如,假设我们有一个主协程`main`,它要并发执行两个子协程`task1`和`task2`,我们可以这样做: ```python import asyncio async def task1(): await asyncio.sleep(1) print("Task 1 done") async def task2(): await asyncio.sleep(2) print("Task 2 done") async def main(): # 创建任务并启动 t1 = asyncio.create_task(task1()) t2 = asyncio.create_task(task2()) # 等待两个任务都完成 await asyncio.gather(t1, t2) print("All tasks done") asyncio.run(main()) ``` 这样,主协程`main`会等待`task1`和`task2`都完成后才退出。然后`asyncio.run`会关闭事件循环。 但是,用户可能遇到了嵌套使用`asyncio.run`的问题。根据引用[2],在同一个线程中,我们不能嵌套调用`asyncio.run`,因为每次调用`asyncio.run`都会创建一个新的事件循环并关闭旧的事件循环。因此,在已经运行的事件循环中再调用`asyncio.run`会导致错误。 所以,如果用户想要在主协程中运行另一个协程,不应该再使用`asyncio.run`,而是应该使用`await`或者`asyncio.create_task`。 总结: 1. 在同一个线程中,避免嵌套调用`asyncio.run`。 2. 使用`asyncio.create_task`创建任务,然后使用`await asyncio.gather`来等待所有任务完成。 因此,针对用户的问题,解决方法就是:在主协程中,将需要并发执行的子协程封装为任务,然后使用`asyncio.gather`或类似方法等待它们完成。 注意:如果用户希望在主协程中启动一些后台任务,并且不想等待它们完成(即主协程先退出,但后台任务继续运行),这是不允许的。因为当主协程退出时,事件循环会被关闭,所有未完成的任务都会被取消。所以,一定要确保在主协程退出前,所有任务都已经完成。 如果用户希望主协程在启动任务后,能够处理其他事情,并在最后等待任务完成,那么可以将任务的创建和等待分开。例如: ```python async def main(): task1 = asyncio.create_task(long_task1()) task2 = asyncio.create_task(long_task2()) # 在等待任务完成之前,可以做一些其他事情 await asyncio.sleep(0.5) print("Doing something else...") # 然后等待任务完成 await task1 await task2 ``` 或者使用`asyncio.gather`一次性等待多个任务。 因此,用户的问题的解决方法是:避免嵌套`asyncio.run`,使用`asyncio.create_task`和`asyncio.gather`来管理并发任务并等待它们完成。</think>### asyncio.run 主协程等待子协程完成的解决方法 在 Python 的异步编程中,`asyncio.run()` 会等待**主协程**完成,但不会自动等待主协程内部创建的**子协程**完成。以下是原因分析和解决方法: #### 问题原因 1. **任务生命周期**:`asyncio.run()` 只保证主协程(入口函数)执行完成,不等待其创建的未完成子任务[^1][^3]。 2. **事件循环关闭**:当主协程返回时,事件循环会立即关闭,所有未完成的子任务会被取消[^3][^4]。 3. **显式等待需求**:子协程需要显式等待(`await`)或封装为任务(`Task`)并通过特定机制管理[^3][^5]。 --- ### 解决方案 #### 方法 1:使用 `asyncio.gather()` 等待所有子任务 ```python import asyncio async def sub_task(): await asyncio.sleep(1) print("子任务完成") async def main(): # 创建并等待所有子任务 await asyncio.gather( sub_task(), sub_task(), sub_task() ) print("主协程完成") asyncio.run(main()) ``` **效果**: - 主协程会等待所有 `gather()` 中的子任务完成 - 所有任务并发执行,总耗时 ≈ 1秒(而非顺序执行的3秒) #### 方法 2:手动管理任务对象 ```python async def main(): tasks = [ asyncio.create_task(sub_task()), asyncio.create_task(sub_task()) ] # 显式等待所有任务完成 await asyncio.wait(tasks) print("主协程完成") ``` **优势**:更灵活的任务控制(如添加超时、单独取消等)。 #### 方法 3:嵌套 `await`(顺序执行) ```python async def main(): await sub_task() # 等待子任务1完成 await sub_task() # 再执行子任务2 ``` **注意**:此方法导致任务**顺序执行**,失去并发优势[^3][^5]。 --- ### 关键原则 1. **任务封装**:子协程需通过 `asyncio.create_task()` 或 `asyncio.gather()` 转为任务[^3][^4]。 2. **显式等待**:主协程必须显式 `await` 任务集合[^5]。 3. **避免嵌套事件循环**:禁止在 `asyncio.run()` 内部再调用 `asyncio.run()`(会导致 `RuntimeError`)[^2][^4]。 > 通过 `asyncio.gather()` 或 `asyncio.wait()` 管理子任务,可确保主协程等待所有子任务完成后再退出,避免任务被意外取消[^3][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值