PHP 中高效监听标准输入按键的正确实现方法

PHP 中高效监听标准输入按键的正确实现方法

本文介绍如何解决 php 使用非阻塞 `fgets()` 监听 stdin 时导致 cpu 占用率飙升至 100% 的问题,并提供基于 `time_nanosleep()` 的轻量级轮询方案及更优的替代实践。

在命令行 PHP 应用中,常需实时响应用户按键(如菜单导航、交互式工具),开发者常采用 fopen(“php://stdin”) 配合 stream_set_blocking(false) 实现非阻塞读取。但原始写法存在严重性能缺陷:

$stdin = fopen("php://stdin", "r");
stream_set_blocking($stdin, false);
system("stty cbreak -echo");

while (true) {
    if ($keypress = strtoupper(fgets($stdin))) {
        break;
    }
}

该循环会以极高速度反复调用 fgets() —— 当输入缓冲区为空时,fgets() 立即返回 false,CPU 在无休止的“检查→失败→再检查”中满载运行,造成 100% 占用,既耗电又影响系统响应。

推荐修复方案:引入微秒级休眠
通过 time_nanosleep(0, N) 在每次空读之后暂停指定纳秒数(如 5 毫秒 = 5,000,000 纳秒),可将 CPU 占用降至接近 0%,同时保持毫秒级响应灵敏度:

$stdin = fopen("php://stdin", "r");
stream_set_blocking($stdin, false);
system("stty cbreak -echo");

$t = 1000000; // 1 微秒 = 1000 纳秒 → 此处单位为纳秒
while (!($keypress = strtoupper(trim(fgets($stdin))))) {
    time_nanosleep(0, 5 * $t); // 休眠 5 毫秒
}

// 恢复终端设置(关键!)
system("stty -cbreak echo");
stream_set_blocking($stdin, true);
fclose($stdin);

⚠️ 注意事项

跃问视频

跃问视频

阶跃星辰推出的AI视频生成工具

下载

  • 必须调用 trim() 清除换行符,避免误判空行;
  • stty cbreak -echo 关闭回显并启用单字符输入,退出前务必恢复(如 stty -cbreak echo),否则终端可能异常;
  • time_nanosleep() 在 Windows 下不可用,生产环境建议封装兼容逻辑或改用 usleep(5000)(精度略低但跨平台);
  • 若需更高可靠性,可结合 stream_select() 实现真正的 I/O 多路复用(需 PHP ≥ 7.4,且仅限 Unix-like 系统):
$read = [$stdin];
$write = $except = [];
if (stream_select($read, $write, $except, 0, 5000) > 0) {
    $keypress = strtoupper(trim(fgets($stdin)));
}

? 总结:非阻塞轮询必须配合适当延时,time_nanosleep() 是平衡响应性与资源消耗的简洁解法;长期项目建议抽象为可配置的 KeyReader 类,并增加超时、信号中断等健壮性处理。

立即学习PHP免费学习笔记(深入)”;

https://www.php.cn/faq/1992119.html

发表回复

Your email address will not be published. Required fields are marked *