usleep(100) 不精确是因为其依赖系统时钟粒度(如hz=250对应4ms)和调度延迟,实际延迟可能达毫秒级甚至更高,且受负载、i/o、虚拟化等影响显著。

usleep(100) 为什么永远不是精确的100微秒?
因为 usleep() 根本不保证精度——它只是“请求”操作系统暂停,而系统是否真能按微秒响应,取决于底层时钟源和调度机制。你在代码里写 usleep(100),实际延迟可能是 200μs、1.2ms,甚至偶尔跳到 10ms,完全不可控。
- Linux 内核的时钟粒度由
HZ值决定:常见配置为HZ=250(4ms 粒度)或HZ=1000(1ms 粒度),低于这个值的usleep()请求会被向上取整到最近的时钟滴答点 -
usleep()最终调用的是nanosleep()或退化为select(),后者在某些旧系统上连毫秒级都难保障 - PHP 运行在用户态,从调用
usleep()到内核真正挂起进程,中间还有 Zend 执行开销、信号处理、进程上下文切换等不确定延迟
系统负载高时,sleep 类函数会更不准吗?
会,而且偏差会明显放大。高负载本身不会让 sleep() 或 usleep() “变慢”,但它会让进程被调度器推迟唤醒——你设了 usleep(500),结果 CPU 正被一个实时优先级进程霸占,你的脚本就得排队等,醒来时可能已过去 3ms 甚至更久。
- CPU 持续 90%+ 利用率时,
usleep(1000)(1ms)实测偏差常达 ±2–5ms - I/O 密集型负载(如大量磁盘刷写)会加剧中断延迟,进一步拖慢时钟中断响应
- 虚拟机环境(尤其是超配的云主机)中,宿主机调度抖动会直接传导给 PHP 进程,
usleep()表现比物理机更飘忽
怎么查你当前系统的实际时钟精度?
别猜,直接看内核参数和实测。精度不是 PHP 能改的,得从系统层确认底线在哪里。
- 查当前
HZ值:zcat /proc/config.gz | grep CONFIG_HZ(若启用)或getconf CLK_TCK(通常等于HZ) - 粗略实测最小可分辨延迟:
php -r "for(\$i=0;\$i —— 多次运行看输出是否稳定在 1000μs 左右 - 检查时钟源:
cat /sys/devices/system/clocksource/clocksource0/current_clocksource,优先选tsc(Intel/AMD 时间戳计数器),避免jiffies(纯软件模拟,粒度最差)
真需要微秒级控制,该换什么?
继续硬刚 usleep() 是徒劳的。PHP 原生函数不提供可靠微秒调度能力,必须换范式。
立即学习“PHP免费学习笔记(深入)”;
- CLI 场景下,用
pcntl_signal()+pcntl_sigwaitinfo()配合timer_create()(需posix扩展)可逼近纳秒级定时,但复杂度陡增 - Web 场景绝对不要用
usleep()控制响应节奏——改用 Swoole 协程的co::sleep(0.001)(毫秒级,非阻塞)或基于 Redis 的延迟队列 - 性能压测或硬件同步类需求,PHP 本身就不是合适语言;应交由 C 扩展或外部服务(如
chrony同步 +busy-loop微调)完成
最常被忽略的一点:哪怕系统时钟源是 tsc,只要 PHP 进程跑在容器里、开了 cgroup CPU 限频、或用了 systemd 的 CPUQuota,usleep() 的误差就会从微秒级滑向毫秒级——这时再调优代码毫无意义,得先看 systemd-analyze blame 和 cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us。











