Python 向循环中添加自身副本的异步IO任务

Python 向循环中添加自身副本的异步IO任务,python,python-asyncio,Python,Python Asyncio,我是asyncio的新手,目前我自己还不能解决这个问题,非常感谢您的帮助 用例如下所示: 有一个web服务,我需要从中获取数据,当向它发送太多请求时,它会阻止和黑名单 我需要向该web服务发出大量数据请求 web服务以分页方式发送数据,即当给定请求的数据太多时,需要发出后续请求以获取更多页面 通过检查特定请求的响应,可以确定是否需要获取更多页面 一旦客户端接收到数据,就需要将其写入磁盘 因此,在我看来,设置可以如下所示: -编制了需要提出的请求的初步清单 -信号量控制单位时间内发出多少请求以

我是asyncio的新手,目前我自己还不能解决这个问题,非常感谢您的帮助

用例如下所示:

  • 有一个web服务,我需要从中获取数据,当向它发送太多请求时,它会阻止和黑名单
  • 我需要向该web服务发出大量数据请求
  • web服务以分页方式发送数据,即当给定请求的数据太多时,需要发出后续请求以获取更多页面
  • 通过检查特定请求的响应,可以确定是否需要获取更多页面
  • 一旦客户端接收到数据,就需要将其写入磁盘
因此,在我看来,设置可以如下所示: -编制了需要提出的请求的初步清单 -信号量控制单位时间内发出多少请求以控制节流。 -所有初始请求都添加到循环中。 -当收到响应时,将调度一个单独的协程(或者线程?)来持久化数据。我不希望持久性阻止获取更多数据。 -当收到响应时,将检查是否需要请求更多页面以获取完整数据。如果需要更多页面,则会向循环中添加另一个任务以获取下一页

我已经为一个最小的示例编写了一些代码,它应该为我试图实现的目标提供框架:

import asyncio
import time 
import datetime
from random import random

sema = asyncio.Semaphore(2)

async def my_worker():
    async with sema:
        print("{}".format(datetime.datetime.now()))
        print("I'm going to fetch some data")
        result = await data_fetcher()

        print("I'm going to save data to disk")
        await write_result_to_disk(result)

        if random() > 0.5:
            print("I need to create and run a new worker here to fetch more data")

async def data_fetcher():
    await asyncio.sleep(3)
    return "Bla bla bla"

async def write_result_to_disk(result):
    await asyncio.sleep(3)
    print(result)

blah = [my_worker(), my_worker(), my_worker(), my_worker(), my_worker()]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*blah))
print("All Workers Completed")
loop.close()
这似乎可以正确设置信号量并运行workers,但有几个问题没有回答:

  • 首先也是最重要的一点,我如何动态地向循环中添加更多的工作者(以获取后续页面)
  • 如何处理持久性,使其不会阻止数据获取
  • 假设多个页面需要在同一个文件中结束,我如何安全地从这些请求收集所有数据,合并它们,然后在不阻止其他数据获取请求的情况下保持这些数据
提前感谢您的帮助

首先也是最重要的一点,我如何动态地向循环中添加更多的工作者(以获取后续页面)

您可以使用将新的协同路由排队到事件循环

如何处理持久性,使其不会阻止数据获取

如果你说的是向数据库中写入数据,那么就有相应的库。如果您正在谈论写入文件,那么这很棘手——本地文件IO几乎总是阻塞的,所以您必须将工作委托给单独的线程。幸运的是,asyncio提供了一个帮助程序:

假设多个页面需要在同一个文件中结束,我如何安全地从这些请求收集所有数据,合并它们,然后在不阻止其他数据获取请求的情况下保持这些数据


这开始超出了对SO来说是个好问题的范围。对此,您应该仔细阅读不同的并发模式。

对于收集和合并,我建议您使用一个队列,在接收数据时将数据放入其中,并让另一个线程中的工作线程不断获取数据并将其附加到文件中。谢谢您的回答。你能把它放在我提供的代码的上下文中吗?具体来说,my_worker()如何生成新的my_worker(以获取下一页)?我的\u worker()如何访问循环以调用loop.run在\u executor()中运行?谢谢。@llevar您可以使用
loop=asyncio获得当前正在运行的事件循环。get\u event\u loop()
@llevar另外,如果您的写入量不大,您可能不必麻烦使用
run\u in\u executor
,因为操作系统将把内容写入页面缓存并在后台刷新。谢谢您的反馈。我仍然不清楚如何实现我的主要目标。如果我调用Survey_future()在my_worker()中传入my_worker(),但我没有等待它,则会出现一个错误,即循环已完成,但某些未来仍处于挂起状态。如果我确实在等待新工人,那么在新工人完成之前,原始工人无法完成。目标是从当前运行的worker中调度一个新的worker,将其添加到循环中,并继续执行。