
后端【免费下载链接】geventCoroutine-based concurrency library for Python项目地址https://gitcode.com/gh_mirrors/ge/gevent点击查看免费下载本指南以 gevent 官方仓库中的 concurrent_download.py 示例对应文档 docs/examples/concurrent_download.rst为主线讲解如何用 gevent 实现并发发起多个 HTTP/HTTPS 请求并等待全部完成的经典并发模型。读完本文你将掌握gevent.spawn创建协程任务、gevent.wait批量等待、monkey.patch_all让标准库 I/O 变为协作式的完整调用链并理解该示例在仓库测试体系中的定位与运行方式。示例概述仓库中最小的并发 I/O 示范examples/目录是 gevent 官方随源码分发的一组可运行示例docs/examples/下的.rst文档则是这些示例的代码快照。从 docs/examples/index.rst 的注释可以看到这些文档均由一行 shell 命令批量生成将examples/*.py逐个通过literalinclude指令嵌入对应的.rst文件。因此 docs/examples/concurrent_download.rst 的正文主体就是 examples/concurrent_download.py 的完整源码并以:linenos:选项展示行号——阅读本文时建议对照源码文件逐行查看。该示例在examples/目录与dns_mass_resolve、echoserver、webproxy等并列中承担的角色是Hello World 级别的并发下载不依赖任何第三方 Web 框架仅用geventrequests就实现三个 HTTPS 站点的并行抓取。其核心价值在于用不到 30 行代码浓缩了 gevent 编程的三步范式全局打补丁 → 批量派生协程 → 统一等待完成。完整源码与逐段拆解以下是 examples/concurrent_download.py 的完整代码与原文档呈现内容一致#!/usr/bin/python # Copyright (c) 2009 Denis Bilenko. See LICENSE for details. # gevent-test-requires-resource: network Spawn multiple workers and wait for them to complete from __future__ import print_function import gevent from gevent import monkey # patches stdlib (including socket and ssl modules) to cooperate with other greenlets monkey.patch_all() import requests # Note that were using HTTPS, so # this demonstrates that SSL works. urls [ https://www.google.com/, https://www.apple.com/, https://www.python.org/ ] def print_head(url): print(Starting %s % url) data requests.get(url).text print(%s: %s bytes: %r % (url, len(data), data[:50])) jobs [gevent.spawn(print_head, _url) for _url in urls] gevent.wait(jobs)第 3 行测试资源声明注释# gevent-test-requires-resource: network这行注释不是普通注释而是 gevent 测试框架的资源标记。在 src/gevent/tests/test__examples.py 中测试运行器会读取每个示例文件用正则^# gevent-test-requires-resource: (.*)$提取所需资源当测试环境被配置为跳过网络资源时gevent.testing.resources.skip_without_resource该示例会被整体跳过避免在离线 CI 环境中产生假失败。第 10 行monkey.patch_all() 让标准库 I/O 协作化monkey.patch_all()这是 gevent 编程中最重要的一行其定义位于 src/gevent/monkey/init.py 的patch_all函数第 640 行起。它的作用是把标准库中阻塞式的 I/O 模块替换为 gevent 的协作式实现使socket、ssl、select、time、thread、os、subprocess、queue等模块在被调用时主动让出控制权给事件循环hub从而实现代码几乎不改、I/O 自动并发。从函数签名可以看到所有可选开关及其默认值def patch_all(socketTrue, dnsTrue, timeTrue, selectTrue, threadTrue, osTrue, sslTrue, subprocessTrue, sysFalse, aggressiveTrue, EventTrue, builtinsTrue, signalTrue, queueTrue, contextvarsTrue, **kwargs):几点值得注意的细节均来自源码 docstringsys默认Falsegevent 默认不 patchsys模块避免对解释器全局状态造成过大影响重复调用会累积补丁1.1 版本起若多次以不同参数调用patch_all后续调用只会追加补丁而不会撤销已安装的补丁且会发出警告osFalse与signalTrue的坑源码明确警告只 patchsignal而不 patchos会导致后续安装的 SIGCHLD 处理器不被调用、os.waitpid在配合gevent.subprocess使用时可能异常未来版本变更预告docstring 注明计划在 2027 年初发布的版本中将所有参数改为仅限关键字传递keyword-only因此新代码应始终使用patch_all(socketTrue, ...)的关键字写法避免位置参数调用。在本示例中patch 的核心价值在于requests库底层走socket/ssl这两个模块被 patch 后requests.get()发出的网络请求在等待响应时会让出 CPU从而使多个 greenlet 可以并行推进而不是串行等待。第 1620 行任务清单urls [ https://www.google.com/, https://www.apple.com/, https://www.python.org/ ]三个目标站点全部使用 HTTPS正如源码注释所说were using HTTPS, so this demonstrates that SSL works——即证明 monkey patch 之后ssl握手、加密通信同样能协作式并发而不会阻塞整个进程。第 2427 行单个任务函数def print_head(url): print(Starting %s % url) data requests.get(url).text print(%s: %s bytes: %r % (url, len(data), data[:50]))这是每个 greenlet 要执行的工作单元打印开始信息、发起请求、打印响应体大小与前 50 个字符。print的三个调用恰好构成一个完整的可观测时序——你能在输出中看到Starting url按 spawn 顺序出现而各站的bytes结果则按网络响应到达顺序出现直观地体现并发而非串行。第 29 行gevent.spawn 批量派生协程jobs [gevent.spawn(print_head, _url) for _url in urls]gevent.spawn的底层实现位于 src/gevent/greenlet.py 的Greenlet.spawn类方法第 678694 行classmethod def spawn(cls, *args, **kwargs): spawn(function, *args, **kwargs) - Greenlet Create a new :class:Greenlet object and schedule it to run function(*args, **kwargs). This can be used as gevent.spawn or Greenlet.spawn. ... g cls(*args, **kwargs) g.start() return g即创建 Greenlet 对象 立即调用g.start()注册到 hub 调度器。start()内部通过self._start_event.start(self.switch)把该 greenlet 的入口绑定到事件循环上使其在下一个事件循环迭代中被执行——所以spawn只是排定任务真正的运行发生在控制权交还给 hub 之后。同时注意自 1.1b1 起如果传入的函数不可调用spawn会立即抛出TypeError而不是等到 greenlet 运行时才失败这有助于尽早暴露错误。与之配套的Greenlet.spawn_later(seconds, function, ...)第 696717 行则允许延迟调度g.start_later(seconds)会在指定秒数后于未来的一次循环迭代中运行该任务适用于定时抓取、重试退避等场景。第 31 行gevent.wait 统一等待gevent.wait(jobs)gevent.wait是等待一组 greenlet 全部完成的入口。它的批量封装joinall定义在 src/gevent/greenlet.py 第 1069 行签名与语义如下节选自源码 docstring 所述能力def joinall(greenlets, timeoutNone, raise_errorFalse, countNone):若给定timeout超过该秒数后返回尚未完成的 greenlet 保留在列表中若raise_errorTrue任一 greenlet 抛出异常会立即向外抛出若给定count则只要已有count个 greenlet 完成即返回无需等待全部。示例中没有传任何参数即等所有下载任务结束再退出主程序——这是避免主流程结束但协程未跑完的关键一步若不调用wait/joinall脚本可能在 greenlet 真正完成网络请求之前就退出。运行方式与预期输出在安装好 gevent以及示例依赖的requests的环境中直接运行python examples/concurrent_download.py典型输出形如Starting https://www.google.com/ Starting https://www.apple.com/ Starting https://www.python.org/ https://www.python.org/: 48317 bytes: !doctype html... https://www.apple.com/: 118279 bytes: !doctype html... https://www.google.com/: 5964 bytes: !doctype html...注意三点观察技巧三个Starting几乎同时打印证明三个 greenlet 都已进入调度队列bytes结果的顺序与 URL 列表顺序不一致恰好反映真实网络延迟而非串行执行脚本会等到三个请求全部返回后才退出这正是gevent.wait(jobs)的保障。由于示例需要访问外网如果网络不可用或 DNS 不通脚本会因请求异常终止——这也是测试框架将network资源标记剥离出来的原因。想体验不依赖外网的变体可以把urls换成局域网内可达的地址或参考同目录的 echoserver.py 先起一个本地服务。示例的测试保障机制gevent 仓库并不是示例只负责展示——它们被纳入了真实的自动化测试。查看 src/gevent/tests/test__examples.py 可以发现两层机制第一层通用测试。测试运行器遍历examples/*.py对每个示例做超时执行校验。其中专门为concurrent_download.py配置了时间范围第 3943 行default_time_range (2, 10) time_ranges { # what is this even supposed to mean? pylint:disableconsider-using-namedtuple-or-dataclass concurrent_download.py: (0, 30), processes.py: (0, default_time_range[-1]) }即允许concurrent_download.py在 030 秒内完成测试宽于默认的 210 秒区间——这是考虑到真实网络请求的延迟抖动。这个细节也提醒使用者并发下载示例的执行时间由网络质量决定不要为它设置过严的超时。第二层专用测试。若仓库中已存在名为test__example_文件名.py的专用测试则通用测试会跳过该文件见_find_files_to_ignore逻辑。这种通用兜底 专用深入的测试布局保证了示例代码随版本演进始终可运行。从示例走向生产并发任务编排的三种演化concurrent_download展示的是最朴素的一次性并发模型而仓库中还有更多示例展示其演化形态可作为你写真实抓取程序的进阶参照。演化一限制并发规模的 Pool。dns_mass_resolve.py 展示了如何同时发起 1000 个解析任务但用Pool(10)将并发上限定为 10from gevent.pool import Pool pool Pool(10) ... for x in range(10, 10 N): pool.spawn(job, %s.com % x) pool.join()当抓取目标数量大如爬取整个站点时用 Pool 而非裸 spawn 可以控制连接数避免压垮目标服务或触发本机文件描述符耗尽。pool.spawn与gevent.spawn的调用方式完全一致但任务会进入池内排队执行。演化二延迟调度。需要定时抓取或失败重试时用gevent.spawn_later(seconds, func, *args)其定义同样在 src/gevent/greenlet.py 中底层通过start_later注册一个定时器事件。演化三带超时的批量等待。把示例第 31 行改为gevent.wait(jobs, timeout10)即可让脚本在 10 秒后放弃未完成的请求继续退出而gevent.joinall(jobs, raise_errorTrue)则可在任一请求失败时立即向外抛出异常方便快速失败fail-fast。常见问题与注意事项Q1不调用monkey.patch_all()会怎样requests.get()会使用标准库阻塞式 socket三个 greenlet 实际变成串行执行——每个请求必须等前一个完全返回才开始下一个并发退化为顺序。这也是示例将patch_all放在import requests之前的用意确保 requests 在导入时就使用已被替换的 socket/ssl 实现。若先 import 后 patchrequests 内部已绑定的部分引用可能仍指向原实现具体行为取决于其导入时机因此务必保持先 patch、后导入依赖库的顺序。Q2gevent.spawn之后立即发生什么如 src/gevent/greenlet.py 的spawn所示greenlet 被创建并start()但只有当前执行让出控制权例如本示例的gevent.wait时hub 才开始驱动这些 greenlet 运行。所以spawn是排定wait才是真正跑起来并等完。Q3示例能在 CI 或离线环境跑吗官方测试通过# gevent-test-requires-resource: network标记把该示例划为网络依赖测试离线环境会被自动跳过。你自己的脚本若在受限网络环境运行应自行替换 URL 或准备本地服务并考虑为请求设置超时。Q4HTTPS 是否同样受益受益。patch_all默认 patchssl模块SSL 握手与加密读写同样是协作式 I/O示例故意选用三个 HTTPS 站点正是为了验证这一点。小结examples/concurrent_download.py 虽然只有 28 行却完整覆盖了 gevent 编程的核心三要素monkey.patch_all()I/O 协作化、gevent.spawn任务排定、gevent.wait统一等待。对照 src/gevent/greenlet.py 中spawn/spawn_later/joinall的实现、src/gevent/monkey/init.py 中patch_all的默认参数以及 src/gevent/tests/test__examples.py 的测试约束你可以把这个最小示例无缝扩展为带并发上限Pool、带超时wait timeout、带延迟调度spawn_later的生产级并发下载程序。仓库同目录下的 dns_mass_resolve.py、threadpool.py 等示例可继续作为你深入 gevent 并发编排的下一站。赞分享后端【免费下载链接】geventCoroutine-based concurrency library for Python项目地址https://gitcode.com/gh_mirrors/ge/gevent点击查看免费下载相关推荐Celery gevent 并发池实战从示例应用理解 -P gevent 的安装、配置与底层实现Celery gevent 并发池实战从示例应用理解 P gevent 的安装、配置与底层实现 本文以 Celery 仓库中 examples/gevent任务调度后端消息队列gevent 并发批量 DNS 解析实战解读 dns_mass_resolve.py 示例与 GEVENT_RESOLVER 解析器切换gevent 并发批量 DNS 解析实战解读 dns_mass_resolve.py 示例与 GEVENT_RESOLVER 解析器切换 导读 本文围绕 ge后端BAML 并发基准实战解析 parallel-sum-4x250k 工作负载与 spawn/await 并发模型BAML 并发基准实战解析 parallel sum 4x250k 工作负载与 spawn/await 并发模型 导读 parallel sum 4x250k编程语言AI Agent编译器CLI人工智能上一篇Mermaid Live Editor 完整使用指南实时编辑、分享与导出 Mermaid 图表下一篇免Root的Android虚拟定位FakeLocation按应用伪装位置完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考