PHP stream_select 怎样保存循环中的剩余超时时间
如果把固定的 stream_select() 超时时间直接放进循环,每轮调用都会重新获得一段完整等待时间,整个循环就可能比预期拖得更久。保存“剩余超时时间”的关键不是修改 PHP 内部状态,而是先确定一个截止时刻;每轮开始时用当前时间重新计算剩余微秒数,再拆成秒和微秒传给选择器。
官方文档:https://www.php.net/manual/en/function.stream-select.php
下面的示例只围绕一个读取循环展开:监听数组每次都从原始集合复制,超时每次都由截止时间计算。示例输出是根据代码逻辑推导的说明,不代表本文发布前执行过该程序。
不要把“本轮最多等多久”和“整个循环最多持续多久”混成一个变量。前者可以固定,后者必须由截止时间减去当前时间得到。
先把一次等待和总预算分开
stream_select() 的 seconds 与 microseconds 共同组成一次等待的上限,返回 0 表示本次没有流变为就绪,返回正数表示修改后的监听数组里有可处理的流,返回 false 表示调用出错。它不会替循环保存一个“还剩多少时间”的变量。
因此可以把总预算记成 deadline。循环里只做一件时间相关的事:remaining = deadline - microtime(true)。如果剩余值不大于零,直接结束;否则再把它拆成参数。
这段代码最重要的两个变量是 $deadline 和 $originalRead。前者控制全局上限,后者保证下一轮不会因为本次调用删掉未就绪的流。若只把 $seconds 写成固定值,循环就会在每次返回后重新开始一段完整等待。

为什么每轮都要重建监听数组
传给 stream_select() 的数组是输入也是输出:函数返回时,$read 只保留当前可读的流,原来的未就绪成员会被移除。把这份已经被改写的数组直接带进下一轮,会让监听集合逐渐缩小,甚至变成空集合。
正确的边界是“原始集合负责保存意图,工作集合负责承接本轮结果”。$write 和 $except 也遵循同一规则;不监听某类事件时传 null 即可。
0) {
$read = $originalRead;
$write = null;
$except = null;
// 监听集合不能沿用上一轮的返回数组。
$sec = (int) floor($left);
$usec = (int) floor(($left - $sec) * 1_000_000);
$count = stream_select($read, $write, $except, $sec, $usec);
if ($count === false) {
// 错误路径不要把被改写的数组当作下一轮输入。
throw new RuntimeException('stream_select failed');
}
if ($count === 0) {
// 超时没有就绪流,下一轮仍以 deadline 计算剩余时间。
continue;
}
foreach ($read as $ready) {
// 只消费本次返回的就绪流,原始集合仍保持不变。
consume($ready);
}
}
?>
若业务要求收到第一个事件就结束,可以在 foreach 后 break;若要在一个总预算内持续收集事件,就保留循环,并继续重建数组。两种需求的共同点是:不能用返回后的工作数组替代原始集合。
用指标确认超时没有被循环重置
这个问题适合用基线和结果对比来判断,而不是凭感觉看循环是否“变快”。假设总预算为 2.5 秒,期间出现三次可读事件:修复前每轮又传入 2.5 秒,最坏等待上限接近 7.5 秒;修复后每轮传入截止时间减去当前时间,整个等待预算仍约为 2.5 秒,处理事件所占时间会让剩余等待自然减少。
| 观察项 | 固定超时写在循环内 | 截止时间计算剩余值 |
|---|---|---|
| 每轮参数 | 反复从完整预算开始 | 由 deadline 减当前时间 |
| 监听数组 | 容易复用已被改写的数组 | 每轮从原始集合复制 |
| 总等待上限 | 随返回次数放大 | 受同一个截止时间约束 |
| 排查指标 | 总耗时、返回次数 | 总耗时、剩余值、false/0/正数次数 |
建议在业务日志中记录开始时间、截止时间、每次调用前的剩余微秒数、返回值和总循环耗时。这里的指标用于验证时间预算是否按设计递减;不要把某次机器上的耗时当成固定性能承诺。

几个容易让剩余时间失真的边界
- 微秒进位:将小数部分换算为整数微秒时,如果计算结果恰好达到 1,000,000,应重新归一化到下一秒,避免传入不合适的组合。
- 零剩余:剩余时间不大于零时直接退出;不要为了“再检查一次”把零超时放进高频循环,否则会变成忙轮询。
- 返回 false:错误与超时不是同一种结果。记录错误后结束或由上层决定重试,不能把
false当成 0。 - 空集合:如果没有任何要监听的流,先按业务决定退出或改用其他等待方式;不要把空集合当成正常事件。
- 读取耗时:
foreach内的处理也会消耗总预算,因此下一轮应该继续从截止时间计算,而不是补回固定时长。
相关问题
为什么不用每轮都传同一个 seconds? 因为该参数是本次调用的等待上限,函数不会知道你的循环总预算。固定值会随着返回次数重复计算。
stream_select 返回 0 是否就是错误? 不是。0 表示这次等待到期但没有流就绪;false 才是调用失败,应走错误处理路径。
归纳起来,循环超时的可靠写法只有两条主线:用截止时间保存总预算,用原始集合保存监听意图。每轮从它们派生工作数组和剩余秒、微秒参数,就能避免等待时间被无意重置。
PHP Generator 怎样用 yield 逐批读取数据
- 上一篇
- PHP Generator 怎样用 yield 逐批读取数据
- 下一篇
- PHP fgetcsv 怎样保留引号字段中的换行
-
- 文章 · php教程 | 1小时前 |
- PHP OPcache 预加载怎样评估启动边界
- 452浏览 收藏
-
- 文章 · php教程 | 1小时前 | 依赖管理 · 部署 · PHP · Composer composer.lock PHP平台依赖 config.platform
- PHP Composer 怎样按平台条件锁定依赖
- 271浏览 收藏
-
- 文章 · php教程 | 1小时前 | 数据类型 · PHP · php JSON JSON_BIGINT_AS_STRING json_decode 大整数
- PHP json_decode 怎样避免大整数被转换为浮点数
- 264浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP finally 怎样保证临时文件在 return 前关闭
- 357浏览 收藏
-
- 文章 · php教程 | 2小时前 | 反射 · 参数 · PHP · php 反射 参数元数据 ReflectionParameter Attribute getAttributes
- PHP Attribute 怎样为参数添加机器可读元数据
- 463浏览 收藏
-
- 文章 · php教程 | 2小时前 | PHP · php Fiber Fiber::suspend Fiber::resume
- PHP Fiber 怎样把深层调用栈暂停后恢复
- 185浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 550次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 602次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 628次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 582次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 407次使用
-
- 深入了解Golang网络编程Net包的使用
- 2023-01-23 215浏览
-
- GogRPC超时控制Deadlines用法详解
- 2023-01-07 122浏览
-
- gRPC的发布订阅模式及REST接口和超时控制
- 2023-01-07 112浏览
-
- golang实现PHP数组特性的方法
- 2023-02-16 371浏览
-
- 一文搞懂如何实现Go 超时控制
- 2022-12-30 273浏览

