我有一个Python脚本,它使用asyncio来生成超过一百万个请求。我首先遇到了内存问题然后发现了信号量,我已经成功地实现了一个信号量,以便一次限制并发任务的数量以及队列中的任务。
我的程序会加载一个这样的请求列表:
with open(wordlist) as words:
w = words.read().splitlines()
然后该列表将传递给以下函数进行处理,并完成实际工作。
async def _process_dns_wordlist(self, wordlist, domain):
"""Takes a list of words and adds them to the task list as space is available"""
for word in wordlist:
# Wait on the semaphore before adding more tasks
await self.sem.acquire()
host = '{}.{}'.format(word, domain)
task = asyncio.ensure_future(self._dns_lookup(host))
task.add_done_callback(functools.partial(self._dns_result_callback, host))
self.tasks.append(task)
await asyncio.gather(*self.tasks, return_exceptions=True)
在我实现信号量之前,当我排队所有任务时,程序会崩溃,内存耗尽,现在它会运行一段时间然后崩溃,因为它耗尽内存大约需要通过请求的1/2。
我认为这是因为在我的回调处理了未来后,它会在内存中浪费空间。我的问题是,一旦我完成它,我无法弄清楚用什么来删除已处理的未来。我已经阅读了asyncio文档,但我没有看到破坏/删除方法。我错过了一些非常明显的东西吗
感谢您的帮助!
答案 0 :(得分:1)
事实证明答案很简单,但我不确定这是否是正确的方法。
在我的回调中,在我完成处理结果后,我做了这个:
self.tasks.remove(future)
这成功解决了我的记忆问题。如果您有更好的方法来解决这个问题,请告诉我们!
答案 1 :(得分:0)
您是否出于某些原因存储任务?据我了解,每个任务对象都由eventloop保留。当您将另一个副本存储在self.tasks中时,在eventloop完成它的那一点上,仍然会引用它(在self.tasks中)。这样可以避免将来(完成的事情)被垃圾收集。
如果您将self.tasks
更改为本地tasks
变量以进行收集,然后超出范围,我想代码也可以工作,并且内存使用较少。
与删除回调中的future相比,这将更有效(并且更具可读性),但具有相同的净效果。