使用 asyncio.run() 时关闭 Asyncio 事件循环

Jay*_*K23 9 python python-3.x python-asyncio

我开始使用 AsyncIO 和 AioHTTP,我正在编写一些基本代码来熟悉语法。我尝试了以下应该同时执行 3 个请求的代码:

import time
import logging
import asyncio
import aiohttp
import json
from aiohttp import ClientSession, ClientResponseError
from aiocfscrape import CloudflareScraper

async def nested(url):
    async with CloudflareScraper() as session:
        async with session.get(url) as resp:
            return await resp.text()

async def main():
    URL = "https://www.binance.com/api/v3/exchangeInfo"
    await asyncio.gather(nested(URL), nested(URL), nested(URL))

asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)

这是输出:

raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Run Code Online (Sandbox Code Playgroud)

我不明白为什么我会收到这个错误,有人可以帮我解决这个问题吗?

jup*_*bjy 8

我终于想出了如何继续ProactorEventLoop运行,防止不成功的 IO 关闭。

真的不知道为什么 Windows 的事件循环如此错误,因为这也发生在asyncio.open_connection和asyncio.start_server。

要解决此问题,您需要在永久循环中运行事件循环并手动关闭。

以下代码将涵盖 Windows 和其他环境。

import asyncio
from aiocfscrape import CloudflareScraper


async def nested(url):
    async with CloudflareScraper() as session:
        async with session.get(url) as resp:
            return await resp.text()


async def main():
    await nested("https://www.binance.com/api/v3/exchangeInfo")


try:
    assert isinstance(loop := asyncio.new_event_loop(), asyncio.ProactorEventLoop)
    # No ProactorEventLoop is in asyncio on other OS, will raise AttributeError in that case.

except (AssertionError, AttributeError):
    asyncio.run(main())
    
else:
    async def proactor_wrap(loop_: asyncio.ProactorEventLoop, fut: asyncio.coroutines):
        await fut
        loop_.stop()

    loop.create_task(proactor_wrap(loop, main()))
    loop.run_forever()
Run Code Online (Sandbox Code Playgroud)

此代码将检查 newEventLoop是否为ProactorEventLoop.
如果是这样,永远保持循环直到proactor_wrap等待main和调度循环停止。

否则——可能是除 Windows 之外的所有其他操作系统——不需要这些额外的步骤,只需调用即可asyncio.run()。

像 Pycharm 这样的 IDE 会抱怨传递AbstractEventLoop给ProactorEventLoop参数,可以安全地忽略。


Gre*_*reg 8

虽然这已得到答复并接受。您可以通过一行代码解决此问题:asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

Event loop is closed是 Windows 上的一个已知问题(请参阅https://github.com/encode/httpx/issues/914)。我怀疑这个问题会在 Python 的更高版本中得到修复。要解决该错误,只需将事件循环策略设置为 WindowsSelectorEventLoopPolicy 即可。

如果您打算在非Windows环境下运行代码;那么你需要添加一个 if 语句来防止错误。例如:if sys.platform == 'win32'。或者添加代码来设置策略。

工作示例:

import asyncio
from aiocfscrape import CloudflareScraper
import sys

async def nested(url):
    async with CloudflareScraper() as session:
        async with session.get(url) as resp:
            print(resp.status)
            return await resp.text()

async def main():
    URL = "https://www.binance.com/api/v3/exchangeInfo"
    await asyncio.gather(nested(URL), nested(URL), nested(URL))

# Only preform check if your code will run on non-windows environments.
if sys.platform == 'win32':
    # Set the policy to prevent "Event loop is closed" error on Windows - https://github.com/encode/httpx/issues/914
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)