Python aiohttp:在满足条件时取消异步执行

Python aiohttp:在满足条件时取消异步执行,python,python-3.x,python-asyncio,aiohttp,Python,Python 3.x,Python Asyncio,Aiohttp,我已经为一个CTF游戏编写了一个异步暴力脚本,如下所示 async def bound_fetch(sem, session, answer): # generating url, headers and json ... async with sem, session.post(url=url, json=json, headers=headers) as response: if response.status == 200: pri

我已经为一个CTF游戏编写了一个异步暴力脚本,如下所示

async def bound_fetch(sem, session, answer):
    #  generating url, headers and json ...
    async with sem, session.post(url=url, json=json, headers=headers) as response:
        if response.status == 200:
            print('Right answer found: %s' % json['answer'])


async def run(words):
    tasks = []
    sem = asyncio.Semaphore(3)
    async with aiohttp.ClientSession() as session:
        for word in words:
            task = asyncio.create_task(bound_fetch(sem=sem, session=session, answer=''.join(word)))
            tasks.append(task)
        print("Generated %d possible answers. Checking %s" % (len(tasks), base_url))
        await asyncio.gather(*tasks)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    future = asyncio.ensure_future(run(possible_answers))
    loop.run_until_complete(future)
我的参考是本教程:

我想知道在aiohttp中这是不是正确的方法,或者我是否把事情弄得太复杂了(因为我不需要处理所有的响应,只需要知道哪个响应的状态是200)?当满足条件(状态代码)时,如何取消处理

我想知道这是不是在aiohttp做这件事的正确方法

您的代码相当地道。在顶层,您可以省略
asyncio.sure\u future
,只需调用
asyncio.run(run(可能的答案))

当满足条件(状态代码)时,如何取消处理

您可以使用事件或未来对象并等待它,而不是使用
gather
。正如您可能知道的那样,
gather
不需要运行协同路由(它们与
create\u task
一起按计划运行),其明确目的是等待所有协同路由完成。基于
事件的同步可能如下所示:

async def bound_fetch(sem, session, answer, done):
    #  generating url, headers and json ...
    async with sem, session.post(url=url, json=json, headers=headers) as response:
        if response.status == 200:
            done.set()
            done.run_answer = json['answer']

async def run(words):
    sem = asyncio.Semaphore(3)
    done = asyncio.Event()
    async with aiohttp.ClientSession() as session:
        tasks = []
        for word in words:
            tasks.append(asyncio.create_task(bound_fetch(
                sem=sem, session=session, answer=''.join(word), done=done)))
        print("Generated %d possible answers. Checking %s" % (len(words), base_url))
        await done.wait()
        print('Right answer found: %s' % done.run_answer)
        for t in tasks:
            t.cancel()

好的,当条件满足时,我通过
raise StopIteration
取消了执行,而不是
wait asyncio.gather(*tasks)
我确实
wait asyncio.wait(tasks,return\u when=asyncio.FIRST\u EXCEPTION)
。如果有人知道一种更优雅的方法,请发布一个答案:)你可能想提出一些不同于
StopIteration
-因为
StopIteration
是由Python使用的,所以将其用作业务例外不是一个好主意。我已经发布了一个答案,展示了如何使用
事件进行同步。非常感谢您的回复。这看起来确实干净多了。我的代码编辑器抱怨asyncio.create_task()之前没有等待,但我认为这是故意的,所以它不是同步的?顺便说一句,我不确定信号灯是否有效——我在这里得到了很多连接。我可以记录一次活动连接的数量吗?嗯,看起来程序以“RuntimeError:Session is closed Task exception was never Retrieve”终止。应该是这样吗?@Phil我觉得你的IDE有点过分热情了。不过,由于你的第二句话,我修改了代码,一旦找到答案,就取消任务。这应该可以解决这两个问题,因为现在使用了
create\u task
的返回值,并且在退出主协同程序之前取消了任务。(我怀疑
asyncio.run
试图“完成”事件循环,该循环导致剩余任务尝试对现已关闭的会话执行某些操作。)请检查这是否解决了问题。是的,它现在可以正常工作。你能确认这里的信号灯使用正确吗?我是从Pawel的网站上复制的,我知道Dijkstra在信号量背后的理论,但我不确定我是否正确使用了它。举个例子:作者在信号量上使用acquire()和release(),这在我的code@Phil您可以使用信号量,因为
async with semaphore
在进入
with
块和
semaphore.release()时会自动执行
等待semaphore.acquire()
退出时。这比手动调用
acquire
release
更可靠,因为它将在异常或其他过早退出函数的情况下正确释放信号量。