php原生不支持协程调度,swoole用c实现协程运行时;yield+generator仅为迭代器,无法自动挂起阻塞i/o,不能支撑高并发。

PHP 本身不支持原生协程调度,Swoole 是唯一在生产环境稳定提供协程能力的扩展——但它不是“PHP 实现的协程调度”,而是用 C 实现、通过扩展暴露给 PHP 的协程运行时。想靠 yield + Generator 模拟调度?那只是用户态协程雏形,没网络 I/O 自动挂起/恢复,扛不住高并发。
为什么不能直接用 yield + Generator 做协程调度
PHP 的 yield 是语法糖,生成器(Generator)本质是迭代器,不带调度器、不接管 I/O、不自动切换上下文。你手动 next() 它才走一步,遇到 file_get_contents() 或 curl_exec() 这类同步阻塞调用,整个进程就卡死,协程数量上不去,根本谈不上高并发。
-
Swoole的协程是抢占式调度(基于epoll/kqueue),遇到co::sleep()、Co\Http\Client、Co\MySQL等调用时,内核自动挂起当前协程,切到下一个就绪协程 - 纯 PHP
Generator无法拦截系统调用,也没办法 hook socket read/write - 社区有
reactphp、amphp等异步方案,但它们是回调/事件驱动,不是协程模型,写法和心智负担完全不同
Swoole\Coroutine 启动后,哪些函数会自动协程化
不是所有函数都能在协程里安全使用。Swoole 通过内置 hook 机制,在 Swoole\Runtime::enableCoroutine() 开启后,将常见阻塞函数替换为协程版本。但 hook 有范围限制,且依赖扩展版本。
- 默认 hook 的函数包括:
stream_socket_client、fread、fwrite、curl_exec(需SWOOLE_HOOK_CURL)、file_get_contents(需SWOOLE_HOOK_FILE) - MySQLi / PDO 默认不协程化,必须用
Swoole\Coroutine\MySQL或Swoole\Coroutine\PDO替代 -
sleep()不会被 hook,要用co::sleep();usleep()同理,得用co::usleep() - PHP 8.1+ 的
Socket扩展函数(如socket_connect)暂未被 Swoole hook,仍会阻塞
协程数量爆满、内存暴涨的典型原因
协程轻量 ≠ 可无限创建。每个协程默认栈空间 256KB(可调),协程函数里持有了大对象、闭包引用了 $this 或全局变量、或忘了 unset() 中间结果,都会导致内存无法回收。
- 避免在协程内长期持有 DB 连接对象、大数组、资源句柄——连接应复用,数据处理完立刻释放
- 不要在协程里
new数百个stdClass或 JSON 解析后的嵌套数组,考虑流式处理或分块 - 用
swoole_table或chan做协程间通信比传大数组更省内存 -
co::gethostbyname()看似轻量,但 DNS 查询失败时可能超时阻塞数秒,建议加timeout参数并设 fallback 逻辑
HTTP 服务中协程错误传播容易被忽略的点
协程内抛出异常,若没被当前协程的 try/catch 捕获,会终止该协程,但不会影响其他协程或主 Server。这看起来“很稳”,实则掩盖问题:日志没打、监控没报、客户端收不到响应。
- 每个协程入口(如
onRequest回调)必须包一层try/catch,至少记录throwable和协程 ID(Co::getuid()) - 别在协程里
exit或die,会导致整个 Worker 进程退出;改用return或抛出特定异常由顶层捕获 -
Co::wait()等待多个协程时,其中一个失败不会中断其余,需手动检查每个返回值是否为false或异常对象 - 协程内调用
echo不会输出到浏览器,HTTP 响应必须显式调用$response->end(),漏掉就等于请求“静默失败”
真正难的不是启动协程,而是在协程生命周期内管住副作用:文件句柄没关、数据库连接没归还、异常没兜底、日志没打全——这些细节堆起来,才是压垮高并发服务的最后一根稻草。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











