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

Python异步函数运行异常:程序无法终止,停滞在索引输出阶段

问题分析与解决方案

程序状态判断

打印完所有链接索引后停滞,程序大概率仍在运行,但处于低效执行或等待异常请求完成的状态,不建议无限制等待,需要排查代码问题。

核心问题点

1. 低效的索引查找

links.index(link) 是O(n)复杂度的操作,100k次循环会导致总时间复杂度达到O(n²),这会让索引打印阶段就消耗大量时间,甚至让你误以为请求阶段已经开始,实际任务还在排队执行查找操作。

2. 一次性创建100k异步任务

直接为100k链接创建全部任务会导致事件循环过载,即便有httpx的连接限制,大量待执行任务也会占用内存,还可能导致请求队列阻塞,拖慢整体执行效率。

3. 未设置请求超时

client.get 未配置超时时间,若某些请求因网络问题、服务器无响应挂起,整个asyncio.gather会一直等待这些未完成的请求,导致程序无法结束。

4. 潜在的参数解析与异常缺失问题

代码中getVouchDetails函数里的params变量未定义(仅注释了##getting params from link),如果实际参数解析逻辑有错误,会导致请求失败或卡住;同时没有异常捕获,单个请求的错误可能扩散导致整体流程停滞。

修复方案

1. 替换索引查找方式

循环时同时获取索引和链接,避免O(n)的index操作:

async def poolVouch(idx, link, client):
    print(idx)
    soup = await getVouchDetails(link, client)
    # 数据提取逻辑
    finalList.append([...])

# 在main中修改循环:
for idx, link in enumerate(links):
    tasks.append(asyncio.create_task(poolVouch(idx, link, client)))

2. 分批执行任务

不要一次性创建100k任务,分批次执行,控制同时运行的任务数量:

async def main(links):
    async with httpx.AsyncClient(limits=limits) as client:
        batch_size = 50  # 根据服务器承受能力调整
        for i in range(0, len(links), batch_size):
            batch = links[i:i+batch_size]
            tasks = [asyncio.create_task(poolVouch(idx, link, client)) for idx, link in enumerate(batch, start=i)]
            await asyncio.gather(*tasks)

3. 添加请求超时

为httpx请求设置合理的超时时间,避免请求无限挂起:

response = await client.get(
    'https://egramswaraj.gov.in/paymentVoucherDetail.do',
    params=params, 
    headers=headers,
    timeout=httpx.Timeout(10.0)  # 10秒超时,可根据情况调整
)

4. 完善参数解析与异常处理

确保params正确从link中解析,同时添加异常捕获,避免单个请求失败导致整个程序卡住:

async def getVouchDetails(link, client):
    try:
        # 补充解析link得到params的逻辑,例如:
        # params = extract_params_from_link(link)
        response = await client.get(
            'https://egramswaraj.gov.in/paymentVoucherDetail.do',
            params=params, 
            headers=headers,
            timeout=httpx.Timeout(10.0)
        )
        response.raise_for_status()  # 抛出HTTP状态码异常
        soup = BeautifulSoup(response.content, 'lxml')
        return soup
    except Exception as e:
        print(f"请求失败: {link}, 错误: {str(e)}")
        return None

async def poolVouch(idx, link, client):
    print(idx)
    soup = await getVouchDetails(link, client)
    if soup:
        # 仅在获取到有效soup时提取数据
        finalList.append([...])

5. 替换asyncio.ensure_future为asyncio.create_task

asyncio.create_task是Python 3.7+推荐的创建任务方式,语义更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:02:42