You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

代码执行后持续触发Asyncio/Aiohttp RuntimeError问题排查

解决「RuntimeError: Event loop is closed」异常

移除代码冗余部分后,Player_Season变量触发管道错误,运行代码时持续抛出如下异常:

Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001A221370AF0>
Traceback (most recent call last):
  File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python39_64\lib\asyncio\proactor_events.py", line 116, in __del__
    self.close()
  File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python39_64\lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python39_64\lib\asyncio\base_events.py", line 751, in call_soon
    self._check_closed()
  File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python39_64\lib\asyncio\base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

可复现的完整代码:

import asyncio
import aiohttp

class NHLTeam:
    async def fetch(self, session, url):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Windows; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"
        }
        async with session.get(url, headers=headers) as response:
            response.raise_for_status()  # Catch HTTP errors
            content = await response.json()
            return content

    async def scrape(self, data):
        print(data)
        async with aiohttp.ClientSession() as session:
            tasks = []
            for player_name, player_ID, year, position in data: #Iterate over each player
                Game_Type = 2 # 2 = Regular Season | 3 = Play offs
                Link = f"https://api-web.nhle.com/v1/player/{player_ID}/game-log/{year}/{Game_Type}"
                tasks.append(self.fetch_and_parse(session, player_name, Link, year, position))
            results = await asyncio.gather(*tasks)
            return results

    async def fetch_and_parse(self, session, player_name, player_url, year, position):
        Player_Season = await self.fetch(session, player_url) #Causes a Pipeline Error
        

team_1_organized_players_data = [['Connor',8483733, 20232024, 'Center'],['Connor',8483733, 20232024, 'Center']]
team_1 = NHLTeam()

async def run_scraping():
    await asyncio.gather(
        team_1.scrape(team_1_organized_players_data)
        #team_2.scrape(team_2_organized_players_data)
    )

asyncio.run(run_scraping())   

异常原因分析

这个异常的核心是异步资源在事件循环关闭后才触发清理。当asyncio.run()执行完毕关闭事件循环后,aiohttp底层的_ProactorBasePipeTransport对象在垃圾回收时,尝试调用已关闭的事件循环的call_soon方法,从而抛出错误。

具体到代码中:

  1. Windows环境下Python 3.9默认使用的Proactor事件循环,与aiohttp的资源清理时序存在兼容性问题;
  2. fetch_and_parse函数仅获取数据但无返回值,导致异步任务的状态不明确,可能引发资源未被及时回收。

解决办法

方法1:修改事件循环策略(Windows专属)

在代码开头设置使用SelectorEventLoop替代默认的ProactorEventLoop,避免资源清理时序冲突:

import asyncio
from asyncio import WindowsSelectorEventLoopPolicy
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())

方法2:完善异步任务逻辑

给fetch_and_parse函数添加明确的返回值,让异步任务的状态更清晰,确保资源被正确回收:

async def fetch_and_parse(self, session, player_name, player_url, year, position):
    Player_Season = await self.fetch(session, player_url)
    # 返回整理后的结果,明确任务完成状态
    return {
        "player_name": player_name,
        "season": year,
        "position": position,
        "game_data": Player_Season
    }

方法3:显式管理ClientSession生命周期

手动控制ClientSession的关闭,确保所有连接在事件循环关闭前彻底释放:

async def scrape(self, data):
    print(data)
    session = aiohttp.ClientSession()
    try:
        tasks = []
        for player_name, player_ID, year, position in data:
            Game_Type = 2
            Link = f"https://api-web.nhle.com/v1/player/{player_ID}/game-log/{year}/{Game_Type}"
            tasks.append(self.fetch_and_parse(session, player_name, Link, year, position))
        results = await asyncio.gather(*tasks)
        return results
    finally:
        await session.close()

修改后的完整代码

import asyncio
from asyncio import WindowsSelectorEventLoopPolicy
import aiohttp

# 设置事件循环策略,解决Windows下资源清理问题
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())

class NHLTeam:
    async def fetch(self, session, url):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Windows; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"
        }
        async with session.get(url, headers=headers) as response:
            response.raise_for_status()
            content = await response.json()
            return content

    async def scrape(self, data):
        print(data)
        async with aiohttp.ClientSession() as session:
            tasks = []
            for player_name, player_ID, year, position in data:
                Game_Type = 2
                Link = f"https://api-web.nhle.com/v1/player/{player_ID}/game-log/{year}/{Game_Type}"
                tasks.append(self.fetch_and_parse(session, player_name, Link, year, position))
            results = await asyncio.gather(*tasks)
            return results

    async def fetch_and_parse(self, session, player_name, player_url, year, position):
        Player_Season = await self.fetch(session, player_url)
        return {
            "player": player_name,
            "season": year,
            "position": position,
            "game_log": Player_Season
        }

team_1_organized_players_data = [['Connor',8483733, 20232024, 'Center'],['Connor',8483733, 20232024, 'Center']]
team_1 = NHLTeam()

async def run_scraping():
    results = await asyncio.gather(
        team_1.scrape(team_1_organized_players_data)
    )
    print(results)

asyncio.run(run_scraping())

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 20:24:55