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

Python 3:使用requests、asyncio和concurrent的多线程请求示例

关于并行请求方案合理性的分析

嘿,这个思路本身是站得住脚的,但得结合你具体用的是asyncio还是concurrent.futures,以及细节处理来判断是否够合理——咱们拆开来唠:

核心合理性基础

不管是用asyncio的Task futures,还是concurrent.futures的线程/进程池futures,针对网络请求这种IO密集型任务做并行处理,都是完全正确的方向:网络请求大部分时间在等响应,并行能把空闲资源利用起来,大幅提升效率。

分场景的细节优化建议

1. 如果你用的是asyncio(异步方案)

  • 最优搭配:一定要用异步HTTP库(比如aiohttp),别用同步的requests——后者会直接阻塞事件循环,等于白搞异步。
  • 必须加的限制:给并发数加个Semaphore(比如限制10-20个并发),不然一下子发几百个请求,要么被目标服务器封IP,要么自己的程序扛不住。
  • 错误处理不能少:直接调用future.result()会在请求失败时抛出异常搞崩程序,建议用asyncio.gather(return_exceptions=True)来捕获异常,后续再过滤无效结果。

举个优化后的小例子:

import asyncio
import aiohttp

async def getUserId(session, name):
    async with session.get(f"https://example.com/users/{name}") as resp:
        resp.raise_for_status()  # 主动抛出HTTP错误
        return (await resp.json()).get("id")

async def main(names):
    # 限制并发数,避免请求过猛
    sem = asyncio.Semaphore(15)
    async with aiohttp.ClientSession() as session:
        async def bounded_request(name):
            async with sem:
                return await getUserId(session, name)
        # 创建所有任务,批量执行并捕获异常
        tasks = [asyncio.create_task(bounded_request(name)) for name in names]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        # 过滤掉异常结果
        valid_ids = [res for res in results if not isinstance(res, Exception)]
        return valid_ids

if __name__ == "__main__":
    user_names = ["alice", "bob", "charlie"]
    print(asyncio.run(main(user_names)))

2. 如果你用的是concurrent.futures(线程/进程池)

  • 选对池类型:网络请求是IO密集型,用ThreadPoolExecutor就够了,ProcessPoolExecutor完全没必要——进程启动开销大,反而拖慢效率。
  • 线程数要合理:max_workers别设得太大(比如10-20就行),太多线程会增加切换开销,反而得不偿失。
  • 错误处理同样重要:在getUserId里加try-except,或者在取future.result()时捕获异常,避免单个请求失败导致整个流程卡壳。

示例代码参考:

import concurrent.futures
import requests

def getUserId(name):
    try:
        resp = requests.get(f"https://example.com/users/{name}")
        resp.raise_for_status()
        return resp.json().get("id")
    except Exception as e:
        print(f"Failed to fetch ID for {name}: {str(e)}")
        return None

def main(names):
    with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
        futures = [executor.submit(getUserId, name) for name in names]
        # 可以用as_completed按完成顺序取结果,也直接用列表推导式
        results = [future.result() for future in concurrent.futures.as_completed(futures)]
        valid_ids = [res for res in results if res is not None]
        return valid_ids

if __name__ == "__main__":
    user_names = ["alice", "bob", "charlie"]
    print(main(user_names))

总结

你的方案核心逻辑是合理的,但要注意以上这些细节优化——尤其是并发限制和错误处理,不然很容易出现“看起来能跑,但一遇到异常或者高并发就崩”的情况。如果是追求极致效率,优先选asyncio + aiohttp的异步方案;如果是想快速实现、依赖同步库,用ThreadPoolExecutor也完全没问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:14:13