Python 异步IO任务已销毁,但它处于挂起状态
我正在使用一个示例程序,它从数据源(csv或rdbms)中读取数据,进行一些转换,并通过套接字将其发送到服务器 但是因为csv非常大,出于测试的目的,我想在几块之后中断读取。 不幸的是,有些地方出了问题,我不知道该怎么解决。也许我必须取消一些,但现在确定在哪里以及如何取消。我得到以下错误:Python 异步IO任务已销毁,但它处于挂起状态,python,asynchronous,python-asyncio,coroutine,event-loop,Python,Asynchronous,Python Asyncio,Coroutine,Event Loop,我正在使用一个示例程序,它从数据源(csv或rdbms)中读取数据,进行一些转换,并通过套接字将其发送到服务器 但是因为csv非常大,出于测试的目的,我想在几块之后中断读取。 不幸的是,有些地方出了问题,我不知道该怎么解决。也许我必须取消一些,但现在确定在哪里以及如何取消。我得到以下错误: Task was destroyed but it is pending! task: <Task pending coro=<<async_generator_athrow without
Task was destroyed but it is pending!
task: <Task pending coro=<<async_generator_athrow without __name__>()>>
问题很简单。您确实提前退出了循环,但异步生成器尚未耗尽(其挂起):
许多
async
资源(如生成器)需要借助事件循环进行清理。当async for
循环停止通过break
迭代异步生成器时,该生成器仅由垃圾收集器清理。这意味着任务处于挂起状态(等待事件循环),但被(垃圾收集器)销毁
最直接的修复方法是显式关闭生成器:
async def main():
i = 0
aiter = readChunks() # name iterator in order to ...
try:
async for chunk in aiter:
...
i += 1
if i > 5:
break
finally:
await aiter.aclose() # ... clean it up when done
可以使用(免责声明:我维护这个库)简化这些模式。允许在完全关闭发电机之前获取固定数量的项目:
import asyncstdlib as a
async def main():
async for chunk in a.islice(readChunks(), 5):
...
如果中断
条件是动态的,则保证在任何情况下进行清理:
import asyncstdlib as a
async def main():
async with a.scoped_iter(readChunks()) as aiter:
async for idx, chunk in a.enumerate(aiter):
...
if idx >= 5:
break
您的
readChunks
正在异步和循环中运行。没有完成程序,你就是在破坏它
这就是为什么异步IO任务已被销毁,但仍处于挂起状态
简言之,异步任务在后台工作,但您通过中断循环(停止程序)终止了它。这是可行的
import asyncio
import json
import logging
logging.basicConfig(format='%(asctime)s.%(msecs)03d %(message)s',
datefmt='%S')
root = logging.getLogger()
root.setLevel(logging.INFO)
async def readChunks():
# this is basically a dummy alternative for reading csv in chunks
df = [{"chunk_" + str(x) : [r for r in range(10)]} for x in range(10)]
for chunk in df:
await asyncio.sleep(0.002)
root.info('readChunks: next chunk coming')
yield chunk
async def send(row):
j = json.dumps(row)
root.info(f"to be sent: {j}")
await asyncio.sleep(0.002)
async def main():
i = 0
root.info('main: starting to read chunks')
async for chunk in readChunks():
for k, v in chunk.items():
root.info(f'main: sending an item')
#await asyncio.gather(send({k:v}))
stuff = await send({k:v})
i += 1
if i > 5:
break
#print(f"item in main via async generator is {chunk}")
##loop = asyncio.get_event_loop()
##loop.run_until_complete(main())
##loop.close()
if __name__ == '__main__':
asyncio.run(main())
。。。至少它运行并完成了
在bugs.python.org/issue38013中描述了通过解除
async for
循环来停止异步生成器的问题,看起来它在3.7.5中已经修复
然而,使用
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(main())
loop.close()
我在Python3.8中得到一个调试错误,但没有异常
Task was destroyed but it is pending!
task: <Task pending name='Task-8' coro=<<async_generator_athrow without __name__>()>>
任务已销毁,但它处于挂起状态!
任务:不幸的是出了问题…
-出了什么问题?这是怎么一回事?你知道你的合作项目是否按照你想要的方式工作吗?你是否试图通过检查中间结果来追踪错误?为什么不使用高级api-asyncio.run()
?@7u5h4r,亚历克斯:是的,我的目的是破坏迭代。但我现在的做法是。。。至少很糟糕。我在寻找合适的方法。所以我想找到取消的方法,在哪里,如何取消,以及取消的部分。我不知道asyncstdlib
,干得好,Mistermiagi!非常感谢。想做一些取消的事情,并将其转换为任务。还不知道是什么和怎么做的。你知道任何详细的,但一步一步的指南,它越来越深入,并引导我通过异步世界?还发现这似乎是工作的w/o“最终”:等待asyncio.gather(发送({k:v}),返回_异常=真)你有调试吗让我试一试-我使用的是3,8.Nothing报告,调试模式打开-我不确定在我的计算机上没有3.7的情况下我是否可以跟踪该问题,我会查看文档。@Mistermiagi-看起来像是-它出现在3.7.1中,然后在3.7.5中修复。忘记提到我使用的是3.7,因为通过使用3.8,我无法安装一些可用C代码的LIB。@spyder-看起来像是在3.7.5中修复的-如果它不会破坏您的库,请升级到3.7.5-3.7.9。
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.run_until_complete(main())
loop.close()
Task was destroyed but it is pending!
task: <Task pending name='Task-8' coro=<<async_generator_athrow without __name__>()>>