Python中多线程发送HTTP请求的三种方案对比

 更新时间:2026年06月12日 08:20:33   作者:detayun  
本文介绍了Python中多线程发送HTTP请求的三种主流方案对比,分别是threading+Queue,ThreadPoolExecutor和asyncio+aiohttp,文内还指出了常见陷阱,希望对大家有所帮助

为什么需要多线程?

单线程发送 100 个请求,每个 0.5 秒,总耗时 50 秒。多线程发送同样 100 个请求,总耗时可能压到 3~5 秒。

差距不是"快一点",是量级 difference

但多线程不是银弹。用错了,比单线程还慢,还容易把对方服务打挂。

三种主流方案对比

方案适用场景上手难度性能上限
threading + Queue简单批量任务⭐⭐中等
concurrent.futures.ThreadPoolExecutor大多数场景首选
asyncio + aiohttp高并发、IO密集⭐⭐⭐⭐最高

结论先给:80% 的场景用 ThreadPoolExecutor 就够了。

方案一:threading + Queue(手动控制)

适合需要精细控制线程数、任务队列的场景。

import threading
import queue
import requests
import time

urls = [f"http://httpbin.org/delay/1?id={i}" for i in range(20)]
result_queue = queue.Queue()

def worker(q):
    while not q.empty():
        url = q.get()
        try:
            resp = requests.get(url, timeout=5)
            result_queue.put((url, resp.status_code))
        except Exception as e:
            result_queue.put((url, str(e)))
        finally:
            q.task_done()

# 启动 5 个线程
threads = []
for _ in range(5):
    t = threading.Thread(target=worker, args=(queue.Queue(),))
    t.start()
    threads.append(t)

# 填入任务
task_queue = queue.Queue()
for url in urls:
    task_queue.put(url)

# 重新分配任务给 worker(简化写法,实际应把 task_queue 传进去)
for t in threads:
    t.join()

while not result_queue.empty():
    print(result_queue.get())

问题:代码啰嗦,手动管理线程生命周期,容易写错。

方案二:ThreadPoolExecutor(推荐)

Python 3.2+ 内置,几行代码搞定

import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import time

urls = [f"http://httpbin.org/delay/1?id={i}" for i in range(20)]

def fetch(url):
    try:
        resp = requests.get(url, timeout=5)
        return url, resp.status_code
    except Exception as e:
        return url, str(e)

t1 = time.time()

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {executor.submit(fetch, url): url for url in urls}
    
    for future in as_completed(futures):
        url, result = future.result()
        print(f"{url} -> {result}")

print(f"耗时:{time.time() - t1:.2f}s")

优点

  • 自动管理线程池,不用手动 start/join
  • as_completed 按完成顺序返回结果,不用等全部完成
  • max_workers 控制并发数,防止把对方打挂

方案三:asyncio + aiohttp(高性能)

适合 上千级别并发,或者你本身就在用异步框架(FastAPI、Sanic 等)。

import asyncio
import aiohttp
import time

urls = [f"http://httpbin.org/delay/1?id={i}" for i in range(20)]

async def fetch(session, url):
    try:
        async with session.get(url, timeout=5) as resp:
            return url, resp.status
    except Exception as e:
        return url, str(e)

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for url, result in results:
            print(f"{url} -> {result}")

t1 = time.time()
asyncio.run(main())
print(f"耗时:{time.time() - t1:.2f}s")

优点:单线程实现高并发,资源占用极低。

代价:学习曲线陡,所有调用链必须是 async 的,混用会出问题。

常见坑

1. max_workers 不是越大越好

workers = 100  # ❌ 大概率更慢,还可能被封 IP
workers = 10   # ✅ 大部分场景够用

经验值:10~20 是 sweet spot。超过 50 基本没收益,还可能触发对方限流。

2. 忘了设 timeout

requests.get(url)  # ❌ 对方不响应,你的线程就永远挂着
requests.get(url, timeout=5)  # ✅ 5秒没响应就放弃

3. 混用 Session 和多线程

requests.Session() 不是线程安全的

session = requests.Session()  # ❌ 多线程共享同一个 session 会出问题

# 正确做法:每个线程自己创建 session
def fetch(url):
    with requests.Session() as s:  # ✅
        return s.get(url).text

或者用 ThreadPoolExecutor 配合 requests 本身就没问题,因为每个 submit 独立执行函数,函数内自己创建 session。

4. 对方有反爬

多线程 = 高频率访问 = 容易触发风控。

应对:

  • 加随机延迟:time.sleep(random.uniform(0.5, 2))
  • 换 User-Agent 池
  • 用代理 IP 池

选型决策树

请求量 < 100?
  → 单线程 + requests 就够了,别过度设计

请求量 100~1000?
  → ThreadPoolExecutor(方案二)

请求量 > 1000 或已在用异步框架?
  → asyncio + aiohttp(方案三)

需要精细控制任务优先级/重试/失败队列?
  → threading + Queue(方案一)或 Celery

一句话总结

多线程发送请求的核心不是"开更多线程",而是控制并发数 + 复用连接 + 设置超时

ThreadPoolExecutor 解决了 80% 的问题,剩下 20% 才需要上 asyncio 或 Celery。

到此这篇关于Python中多线程发送HTTP请求的三种方案对比的文章就介绍到这了,更多相关Python多线程发送HTTP请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python tensorflow实现mnist手写数字识别示例【非卷积与卷积实现】

    Python tensorflow实现mnist手写数字识别示例【非卷积与卷积实现】

    这篇文章主要介绍了Python tensorflow实现mnist手写数字识别,结合实例形式分析了基于tensorflow模块使用非卷积与卷积算法实现手写数字识别的具体操作技巧,需要的朋友可以参考下
    2019-12-12
  • Python的psutil模块详解

    Python的psutil模块详解

    psutil是一个跨平台库,能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息,需要的朋友可以参考下
    2023-05-05
  • opencv python 图像轮廓/检测轮廓/绘制轮廓的方法

    opencv python 图像轮廓/检测轮廓/绘制轮廓的方法

    这篇文章主要介绍了opencv python 图像轮廓/检测轮廓/绘制轮廓的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • 正则表达式中单个字符的匹配方法教程

    正则表达式中单个字符的匹配方法教程

    正则表达式是用于匹配字符串模式的一种强大工具,它使用特定的语法来描述字符串的特征,如单个字符、字符集、重复次数等,这篇文章主要介绍了正则表达式中单个字符匹配的相关资料,需要的朋友可以参考下
    2025-12-12
  • 使用Python找出水仙花数的方法介绍

    使用Python找出水仙花数的方法介绍

    水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数,水仙花数是指一个3位数,本文就给大家简单聊聊如何使用Python找出水仙花数,感兴趣的同学可以参考阅读
    2023-07-07
  • Django Sitemap 站点地图的实现方法

    Django Sitemap 站点地图的实现方法

    这篇文章主要介绍了Django Sitemap 站点地图的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • 4种方法教你利用Python发现数据的规律

    4种方法教你利用Python发现数据的规律

    发现数据的规律是数据分析和数据科学中非常重要的一个步骤。这篇文章主要给大家整理了4个可以发现数据规律的方法,希望对大家有所帮助
    2023-03-03
  • Python实现多脚本处理定时运行

    Python实现多脚本处理定时运行

    这篇文章主要介绍了Python实现多脚本处理定时运行,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-06-06
  • Python开发之迭代器&生成器的实战案例分享

    Python开发之迭代器&生成器的实战案例分享

    在 Python 中,迭代器和生成器都是用来遍历数据集合的工具,可以按需逐个生成或返回数据,从而避免一次性加载整个数据集合所带来的性能问题和内存消耗问题。本文主要和大家分享几个贴近实际运维开发工作中的场景案例,希望对大家有所帮助
    2023-04-04
  • Django数据库(SQlite)基本入门使用教程

    Django数据库(SQlite)基本入门使用教程

    django有默认自带的数据库,当然也可以用其他的数据库,下面这篇文章主要给大家介绍了关于Django数据库(SQlite)基本入门使用教程的相关资料,需要的朋友可以参考下
    2022-07-07

最新评论