我曾经在混合trio 和kivy 时创建了similar pattern,这是异步运行多个协程的演示。
它使用trio.MemoryChannel,大致相当于asyncio.Queue,这里我将其称为queue。
主要思想是:
- 用类包装每个任务,具有运行功能。
- 创建类对象自己的异步方法,在执行完成后将对象自身放入
queue。
- 创建一个全局任务生成循环以等待
queue 中的对象并为该对象安排执行/创建任务。
import asyncio
import traceback
import httpx
async def task_1(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/")
print(resp.read())
await asyncio.sleep(0.1) # without this would be IP ban
async def task_2(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/meow/")
print(resp.read())
await asyncio.sleep(0.5)
class CoroutineWrapper:
def __init__(self, queue: asyncio.Queue, coro_func, *param):
self.func = coro_func
self.param = param
self.queue = queue
async def run(self):
try:
await self.func(*self.param)
except Exception:
traceback.print_exc()
return
# put itself back into queue
await self.queue.put(self)
class KeepRunning:
def __init__(self):
# queue for gathering CoroutineWrapper
self.queue = asyncio.Queue()
def add_task(self, coro, *param):
wrapped = CoroutineWrapper(self.queue, coro, *param)
# add tasks to be executed in queue
self.queue.put_nowait(wrapped)
async def task_processor(self):
task: CoroutineWrapper
while task := await self.queue.get():
# wait for new CoroutineWrapper Object then schedule it's async method execution
asyncio.create_task(task.run())
async def main():
keep_running = KeepRunning()
async with httpx.AsyncClient() as client:
keep_running.add_task(task_1, client)
keep_running.add_task(task_2, client)
await keep_running.task_processor()
asyncio.run(main())
服务器
import time
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return str(time.time())
@app.route("/meow/")
def meow():
return "meow"
app.run()
输出:
b'meow'
b'1639920445.965701'
b'1639920446.0767004'
b'1639920446.1887035'
b'1639920446.2986999'
b'1639920446.4067013'
b'meow'
b'1639920446.516704'
b'1639920446.6267014'
...
您可以看到任务按照自己的节奏重复运行。
旧答案
似乎您只想循环固定数量的任务。
在这种情况下,只需使用 itertools.cycle 迭代协程列表
但这与同步没有什么不同,所以让我知道你是否需要异步。
import asyncio
import itertools
import httpx
async def main_task(client: httpx.AsyncClient):
resp = await client.get("http://127.0.0.1:5000/")
print(resp.read())
await asyncio.sleep(0.1) # without this would be IP ban
async def main():
async with httpx.AsyncClient() as client:
for coroutine in itertools.cycle([main_task]):
await coroutine(client)
asyncio.run(main())
服务器:
import time
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return str(time.time())
app.run()
输出:
b'1639918937.7694323'
b'1639918937.8804302'
b'1639918937.9914327'
b'1639918938.1014295'
b'1639918938.2124324'
b'1639918938.3204308'
...