Python异步IO取消协同路由
因此,给出了一个复杂的设置,用于生成半并行运行的查询列表(使用信号量以避免同时运行太多查询,从而避免对服务器进行DDoS攻击) 我有一个(本身是异步的)函数,可以创建许多查询:Python异步IO取消协同路由,python,python-asyncio,Python,Python Asyncio,因此,给出了一个复杂的设置,用于生成半并行运行的查询列表(使用信号量以避免同时运行太多查询,从而避免对服务器进行DDoS攻击) 我有一个(本身是异步的)函数,可以创建许多查询: async def run_查询(self,url): 与self.semaphore异步: return wait some_http_lib(url) 异步def create_查询(自我、基本url): #…在实际环境中,收集逻辑要复杂一些 url=等待一些\u http\u lib(base\u url).jso
async def run_查询(self,url):
与self.semaphore异步:
return wait some_http_lib(url)
异步def create_查询(自我、基本url):
#…在实际环境中,收集逻辑要复杂一些
url=等待一些\u http\u lib(base\u url).json()
coros=[self.run_查询url中的url]#注意:还没有执行
返回科罗斯
异步def执行_查询(自):
querys=等待self.create_查询('/seom/url'))
_info(f'prepared{len(querys)}querys')
结果=[]
完成=0
#注意:ofc,在这个简单的示例调用中,这些调用实际上不会异步执行。
#在我使用asyncio.gather的实际情况中,这只会稍微好一点
#可以理解的例子。
对于查询中的查询:
#此时,请求实际上被触发
结果=等待查询
#…一些后处理
如果没有结果['success']:
引发QueryException(结果['message'])#…内部异常
完成+=1
_info(f{len(查询)}查询完成}中的{done}
results.append(结果)
返回结果
现在,它工作得非常好,完全按照我的计划执行,我可以通过中止整个操作来处理其中一个查询中的异常
async def run():
尝试:
return wait QueryRunner.execute_querys()
除查询例外:
_logger.error('发生了可怕的错误')
一无所获
唯一的问题是程序被终止,但留给我的是通常的运行时警告:coroutine QueryRunner.run\u查询从未等待过,因为队列中后面的查询(正确地)没有执行,因此也没有等待
有没有办法取消这些未等待的协同程序?否则是否有可能压制这一警告
[编辑]关于如何在这个简单示例之外执行查询的更多上下文:
查询通常分组在一起,因此有多个调用使用不同的参数创建_querys()。然后,所有收集的组都被循环,并使用asyncio.gather(group)执行查询。这将等待一个组的所有查询,但如果其中一个查询失败,其他组也将被取消,这将导致抛出错误。使用
pending = asyncio.tasks.all_tasks() # < 3.7
以获取挂起任务的列表。你可以和我一起等他们
await asyncio.wait(pending, return_when=asyncio.ALL_COMPLETED)
或取消:
for task in pending:
task.cancel()
因此,您询问如何取消尚未等待或传递给聚集的协同程序。有两种选择:
- 您可以调用
asyncio.create_task(c).cancel()
- 您可以在上直接调用
c.close()
第一个选项有点重(它创建一个任务只是为了立即取消它),但它使用了有文档记录的asyncio功能。第二个选项更轻量级,但也更低级
以上内容适用于从未转换为任务的协同路由对象(例如,通过将它们传递到聚集
或等待
)。如果它们有,例如,如果您调用了asyncio.gather(*coros)
,其中一个已引发,并且您希望取消其余的,您应该更改代码,首先使用asyncio.create_task()
将它们转换为任务,然后调用gather
,最后使用取消未完成的任务:
tasks = list(map(asyncio.create_task, coros))
try:
results = await asyncio.gather(*tasks)
finally:
# if there are unfinished tasks, that is because one of them
# raised - cancel the rest
for t in tasks:
if not t.done():
t.cancel()
wait self.create_querys(…)
看起来不可行,因为create_querys()
返回的是一个列表,而不是一个可等待的对象。@user4815162342它返回一个协程列表。它们(通常)是通过asyncio.gather()连接的,但是为了这个示例,它们在execute_queries中的for循环中等待。哦,对了,我错过了create_queries
本身是异步的。这只是给了我入口点的主要任务,而不是未等待的协程。取消它仍然会打印相同的RuntimeWarningi必须稍微更改收集逻辑以跟踪创建的协程,这在以前是不必要的。这样就可以像你建议的那样取消它们,事实上,效果很好。
tasks = list(map(asyncio.create_task, coros))
try:
results = await asyncio.gather(*tasks)
finally:
# if there are unfinished tasks, that is because one of them
# raised - cancel the rest
for t in tasks:
if not t.done():
t.cancel()