如何使用Asyncio在任何给定时间控制有多少个任务实例正在工作?

时间:2019-06-13 13:31:26

标签: python python-asyncio

我已经用Asyncio制作了两个队列系统。

Responder获取一个链接列表,要求每个链接都有响应,然后将结果放入队列。 Parser从队列中获取响应,对其进行解析,然后将其添加到另一个队列中。 Submitter从队列中获取解析的对象,并将其提交到数据库。

下面的代码显示了我如何创建任务。我为SubmitterParser创建了100个实例。问题似乎是,一旦Submitter达到100个实例,就是这样-提交队列才开始备份。它基本上停止了工作。什么都没有提交了。 ResponderParser通常会继续运行。

submitter完成工作后,如何回收它?我不想为列表中的每个链接创建一个responder。这是我代码不完全理解的部分-for s in submitters: s.cancel() 这会在完成后 杀死我的实例,还是在完成后 杀死我的实例?

async def bulk_submit(not_submitted: set, **kwargs):
    parse_queue = asyncio.Queue()
    submit_queue = asyncio.Queue()
    headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}

    timeout = aiohttp.ClientTimeout(total=60*60)
    async with ClientSession(headers=headers, timeout=timeout) as session:
        tasks=[]
        i=0
        for link in not_submitted:
            i+=1
            tasks.append(
                responder(f'RESPONDER-{i}',url=link, session=session, parse_queue=parse_queue, **kwargs)
            )

        parsers = [asyncio.create_task(parser(f'PARSER-{n}', parse_queue=parse_queue, submit_queue=submit_queue)) for n in range (100)]
        submitters = [asyncio.create_task(submitter(f'SUBMITTER-{n}', submit_queue=submit_queue,)) for n in range (100)]


        await asyncio.gather(*tasks)
        await parse_queue.join()
        await episode_queue.join()
        await submit_queue.join()
        for s in submitters:
            s.cancel()
        for p in parsers:
            p.cancel()

1 个答案:

答案 0 :(得分:0)

  

问题似乎是,一旦Submitter达到100个实例,就这样-提交队列刚刚开始备份。 [...]

     

submitter完成工作后,如何回收它?

您还没有向我们展示submitter的代码,因此很难告诉您如何解决问题,甚至难以精确地解决什么问题。根据您显示的代码,您可能会猜测提交者在处理单个队列项目后只是返回了。您实际上在创建100个并行运行的提交者,但是对队列的访问将它们序列化。当每个人都完成工作时,没有其他人可以耗尽提交队列,工作就停止了。

要解决此问题,您无需回收提交者,只需更改它以保持队列中的项目排队,而不是在获得队列项目后退出。它应该看起来像这样:

async def submitter(name, submit_queue):
    while True:
        item = await submit_queue.get()
        ... process the item ...

使用此设置,您无需创建100个并行运行的提交者,一个就足够了。 (除非您实际上想要一定程度的并行性,也就是说,在这种情况下,您可以创建任意数量的并行提交。)

  

这是我代码不完全理解的部分-for s in submitters: s.cancel()这会在完成所有操作后或实例完成其工作后杀死我的实例吗?

我怀疑在您的代码中取消操作是无操作的,因为在您致电submitter时,您所有的cancel()协程已完成 (取消已完成的任务是忽略)。

通常的想法是在完成工作并且不再需要闲置的工人之后将其杀死。例如,如果submitter包含如上所示的无限循环,则取消prevent it将在bulk_submit返回后永远等待新的队列项(并且永远不会接收它们)。