当前位置:首页 > 文章列表 > 文章 > php教程 > PHP stream_select 怎样保存循环中的剩余超时时间

PHP stream_select 怎样保存循环中的剩余超时时间

来源:17golang原创 2026-10-20 00:10:23 0浏览 收藏

如果把固定的 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 写成固定值,循环就会在每次返回后重新开始一段完整等待。

PHP stream_select 的总预算、截止时间与秒微秒参数静态关系
图1:静态结构说明图,查看总预算、截止时间、剩余微秒数与 stream_select 参数之间的关系;这不是运行截图。

为什么每轮都要重建监听数组

传给 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/正数次数

建议在业务日志中记录开始时间、截止时间、每次调用前的剩余微秒数、返回值和总循环耗时。这里的指标用于验证时间预算是否按设计递减;不要把某次机器上的耗时当成固定性能承诺。

原始监听集合与每轮工作集合的静态关系
图2:集合边界说明图,原始集合保存监听意图,工作集合承接 stream_select 的本轮结果;这不是执行流程截图。

几个容易让剩余时间失真的边界

  • 微秒进位:将小数部分换算为整数微秒时,如果计算结果恰好达到 1,000,000,应重新归一化到下一秒,避免传入不合适的组合。
  • 零剩余:剩余时间不大于零时直接退出;不要为了“再检查一次”把零超时放进高频循环,否则会变成忙轮询。
  • 返回 false:错误与超时不是同一种结果。记录错误后结束或由上层决定重试,不能把 false 当成 0。
  • 空集合:如果没有任何要监听的流,先按业务决定退出或改用其他等待方式;不要把空集合当成正常事件。
  • 读取耗时:foreach 内的处理也会消耗总预算,因此下一轮应该继续从截止时间计算,而不是补回固定时长。

相关问题

为什么不用每轮都传同一个 seconds? 因为该参数是本次调用的等待上限,函数不会知道你的循环总预算。固定值会随着返回次数重复计算。

stream_select 返回 0 是否就是错误? 不是。0 表示这次等待到期但没有流就绪;false 才是调用失败,应走错误处理路径。

归纳起来,循环超时的可靠写法只有两条主线:用截止时间保存总预算,用原始集合保存监听意图。每轮从它们派生工作数组和剩余秒、微秒参数,就能避免等待时间被无意重置。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
PHP Generator 怎样用 yield 逐批读取数据PHP Generator 怎样用 yield 逐批读取数据
上一篇
PHP Generator 怎样用 yield 逐批读取数据
PHP fgetcsv 怎样保留引号字段中的换行
下一篇
PHP fgetcsv 怎样保留引号字段中的换行
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    550次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    602次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    628次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    582次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    407次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码