PHP中高效监听键盘输入的正确实现方式
技术百科
碧海醫心
发布时间:2026-01-17
浏览: 次 本文介绍如何避免php中非阻塞stdin读取导致cpu 100%占用的问题,通过引入微秒级休眠(time_nanosleep)实现低开销轮询,并提供完整、健壮的键盘按键监听方案。
在命令行PHP应用中,若需实时响应用户按键(如制作简易交互菜单或游戏),常采用 fgets() 配合非阻塞流的方式监听 STDIN。但直接使用 stream_set_blocking($stdin, false) 后在 while(true) 中高频调用 fgets(),会导致进程持续空转——每次 fgets() 立即返回 false(无数据),CPU无实际工作却满负荷运转,这是典型的忙等待(busy-waiting)反模式。
正确的做法是:在每次轮询失败后主动让出CPU时间片,避免无效竞争。PHP 提供了高精度睡眠函数 time_nanosleep(),可指定纳秒级休眠(如 5 * 10⁶ 纳秒 = 5 毫秒),既保证响应及时性(人类感知延迟
$stdin = fopen("php://stdin", "r");
if (!$stdin) {
die("无法打开标准输入流\n");
}
// 设置终端为cbreak模式:按键立即生效,不回显
system("stty cbreak -echo 2>/dev/null");
// 关闭流阻塞,启用非阻塞读取
stream_set_blocking($stdin, false);
$key = '';
$t = 1000000; // 1 微秒 = 1000 纳秒,故 10⁶ 纳秒 = 1 毫秒
while (true) {
$line = fgets($stdin);
if ($line !== false) {
$key = strtoupper(trim($line));
break;
}
// 每次无输入时休眠 5 毫秒,显著降低CPU负载
time_nanosleep(0, 5 * $t);
}
// 恢复终端默认行为(重要!防止退出后终端异常)
system("stty -cbreak echo 2>/dev/null");
stream_set_blocking($stdin, true);
fclose($stdin);
echo "捕获到按键: {$key}\n";
⚠️ 关键注意事项:
- 必须恢复终端设置:stty cbreak -echo 会禁用回显和行缓冲,程序退出前务必执行 stty -cbreak echo,否则用户后续终端输入将不可见或需手动重置(如运行 reset 命令);
- 错误处理不可省略:fopen() 和 fgets() 可能失败,应加入基础判空;
- 替代方案参考:对更复杂交互场景,可考虑 ext-posix + pcntl_signal() 结合 select() 实现事件驱动,或使用 readline() 扩展(需启用)获取更安全的行输入支持;
- 跨平台提醒:stty 是Unix/Linux/macOS命令,Windows下需改用 mode con 或依赖 ext-pcntl/第三方库(如 symfony/console 的交互组件)。
综上,仅增加 time_nanosleep() 这一行休眠调用,即可将CPU占用从100%降至忽略不计,同时保持毫秒级响应能力——这是命令行PHP交互开发中简单、有效且必备的优化实践。
# ai
# 这是
# windows
# 中非
# 可将
# 第三方
# 降至
# 不回
# 无法打开
# mac
# 命令行
# win
# linux
# macos
# cos
# stream
# console
# 事件
# while
# select
# php
# echo
# symfony
# fopen
# fgets
# unix
# 将不
# 优化实践
# 这一行
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么更改计算机名_Windows11系统
- C++如何编写函数模板?(泛型编程入门)
- Windows如何使用BitLocker To G
- How to Properly Use NumPy
- php本地部署后数据库连接报错_1045acces
- Win11怎么关闭开机声音_Win11系统启动提示
- php本地部署支持nodejs吗_php与node
- c++中的Tag Dispatching是什么_c
- Python日志系统设计与实现_高可观测性架构实战
- 如何使用Golang实现容器健康检查_监控和自动重
- Win11怎么关闭资讯和兴趣_Windows11任
- Mac如何解压zip和rar文件?(推荐免费工具)
- Win10怎样卸载自带Edge_Win10卸载Ed
- Win11任务栏怎么放到顶部_Win11修改任务栏
- php怎么下载安装后设置错误日志_phpini l
- Python脚本参数接收_sys与argparse
- 如何在JavaScript中动态拼接PHP的bas
- Windows驱动无法加载错误解决方法_驱动签名验
- Win11怎么更改输入法顺序_Win11调整语言首
- 微信企业付款回调PHP怎么接收_处理企业付款异步通
- Win11怎样激活系统密钥_Win11系统密钥激活
- Win10如何设置双wan路由器 Win10双wa
- Win11怎么打开旧版计算器_Win11恢复传统计
- php嵌入式日志记录怎么实现_php将硬件数据写入
- Win11怎么检查TPM2.0模块_Windows
- Mac如何彻底清理浏览器缓存?(Safari与Ch
- php接口返回数据乱码怎么办_php接口调试编码问
- Python文本编码与解码_跨平台解析说明【指导】
- Win11如何更新显卡驱动 Win11检查和安装设
- Win10怎样安装PPT模板_Win10安装PPT
- Python变量绑定机制_引用模型解析【教程】
- Win10怎样清理C盘Steam游戏缓存_Win1
- php485返回数据不完整怎么办_php485数据
- 如何使用Golang反射将map转换为struct
- Mac上的iMovie如何剪辑视频?(新手入门教程
- windows如何备份注册表_windows导出和
- Win11关机快捷键是什么_Win11快速关机方法
- Win10如何更改开机密码_Windows10登录
- Windows10系统怎么查看系统版本_Win10
- Python字符串处理进阶_切片方法解析【指导】
- Win11怎么更改任务栏颜色_Windows11个
- Win11怎么设置任务栏对齐方式_Windows1
- Django密码修改后会话失效的解决方案
- Win11如何设置计划任务 Win11定时执行程序
- Win11如何添加/删除输入法 Win11切换中英
- windows 10应用商店区域怎么改_windo
- 如何使用Golang实现跨域请求支持_Golang
- Win11怎么开启游戏模式_Win11优化游戏帧数
- php条件判断怎么写_ifelse和switchc
- 如何解决Windows字体显示模糊的问题?(Cle


QQ客服