php协程中必须用\swoole\coroutine::sleep()或co::sleep()替代sleep(),因后者会阻塞事件循环、降低qps;前者基于定时器、协程安全,精度约10ms,且不可中断。

PHP 本身没有真正意义上的非阻塞 sleep —— sleep() 和 usleep() 都是同步阻塞调用,会卡住整个进程(包括协程上下文),在 Swoole 或 OpenSwoole 的协程环境中必须换用协程友好的替代方案。
为什么 sleep() 在协程里会拖垮并发
在 Swoole 4.4+ 或 OpenSwoole 的协程中,如果在 go 匿名函数里调用 sleep(1),当前协程确实“让出”了控制权,但底层仍会触发系统级阻塞调用,导致事件循环被干扰、其他协程延迟调度,实测 QPS 可能暴跌 50% 以上。这不是“协程安全”的行为。
真正协程友好的等待,必须基于事件循环的定时器机制,由内核在超时后自动唤醒协程。
-
sleep()/usleep():触发epoll_wait阻塞,协程调度器无法接管 -
Swoole\Coroutine::sleep():注册异步定时器,不阻塞事件循环 - 自建
select()或stream_select()轮询:低效、易出错、不推荐
Swoole\Coroutine::sleep() 是唯一实用选择
这是 Swoole 官方提供的协程安全版 sleep,底层复用 reactor 的毫秒级定时器,开销极小,且能精确到毫秒(注意:不是微秒)。
使用前确认环境:
- 已启用 Swoole 扩展(
php --ri swoole查看版本 ≥ 4.4.0) - 代码运行在协程上下文(如
go(function () { ... })内) - 未在 CLI 模式下误用同步 IO(比如直接
file_get_contents)
示例:
go(function () {
echo "start\n";
\Swoole\Coroutine::sleep(0.1); // 睡 100ms,协程挂起,其他协程继续跑
echo "done\n";
});
注意参数是 float 秒(支持小数),不是整数毫秒 —— 传 100 会睡 100 秒,不是 100ms。
OpenSwoole 用户请用 co::sleep()
OpenSwoole(原 Swoole 分支)为保持向后兼容,同时导出了短名函数 co::sleep(),行为完全一致,但更简洁:
go(function () {
co::sleep(2.5); // 睡 2.5 秒
});
两者本质调用同一底层逻辑,区别仅在于命名空间别名。如果你项目已用 co:: 前缀,无需切换;若混用 Swoole 官方包,优先用全限定名避免歧义。
重要限制:
- 不能在非协程环境调用(如普通 HTTP 请求未开启协程时),会抛出
RuntimeException: must be called in the coroutine - 精度受 reactor 线程 tick 间隔影响,默认 10ms,所以
co::sleep(0.001)实际约 10ms 起步 - 无法被外部中断(比如信号或 cancel),只能等超时——如需可取消等待,得自己封装
Channel+select()
别碰 pcntl_alarm() 或 stream_select() 模拟
有人试图用 PCNTL 信号 + pcntl_signal_dispatch() 搭配 usleep() 轮询实现“伪非阻塞”,或用空 stream_socket_client() + stream_select() 等待超时。这些方案:
- 破坏协程调度模型,极易引发死锁或协程泄漏
- 在高并发下创建大量无效 socket,触发
EMFILE错误 - 精度差、CPU 占用高(轮询)、且与 Swoole 的 reactor 冲突
结论很明确:只要用了 Swoole/OpenSwoole 协程,就只认 \Swoole\Coroutine::sleep() 或 co::sleep()。其他路都是绕远且危险的。
最常被忽略的一点:很多人在协程函数里混用 sleep() 和 co::sleep(),以为“反正都睡”,结果前者悄悄拖慢整个服务。上线前务必全局 grep 一遍 sleep( 和 usleep(,替换成协程版。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











