php.ini 里加:extension=swoole.so
学完这篇你能得到一句话的答案:**要跑多进程用 pcntl,要做高并发常驻服务用 Swoole,想写不绑定扩展的异步代码用 Fibers——但三者根本不是一个层次的东西,选错方向会白干好几天。** 下面按"先分清定位→逐个上手→按场景选型"的顺序讲,每一步都给能直接跑的命令和最小示例。
先分清:三种并发根本不是一回事
| 方案 | 本质 | 运行位置 | 最低要求 |
|---|---|---|---|
| pcntl | 多进程(fork) | 仅 CLI | 编译时带 `--enable-pcntl` |
| Swoole | 常驻内存协程运行时(扩展) | CLI + 自建 HTTP 服务 | 装 PECL 扩展 |
| Fibers | 语言级挂起/恢复原语 | 任意(FPM/CLI) | PHP 8.1+ |
pcntl 是进程级并发,Swoole 是协程 + 事件循环,Fibers 只是语法糖级别的基础设施,自己不带调度器。把 Fibers 当 Swoole 用,是最常见的踩坑。
第一步:pcntl——多进程,只在命令行生效
先确认扩展在:
php -m | grep pcntl
没有就重新编译时加 `--enable-pcntl`,宝塔下可在"软件商店→PHP→安装扩展"里勾选。
最小多进程示例:
$pids = [];
for ($i = 0; $i < 4; $i++) {
$pid = pcntl_fork();
if ($pid === -1) { exit('fork 失败'); }
if ($pid === 0) {
doJob($i); // 子进程干活
exit(0); // 必须 exit,否则子进程会继续跑循环
}
$pids[] = $pid;
}
foreach ($pids as $pid) { pcntl_waitpid($pid, $status); }
注意:pcntl_fork 在 Apache mod_php 的 Web 请求里使用会导致进程状态错乱甚至崩溃,永远不要写在 HTTP 请求处理路径里。FPM 下虽多数环境"能调用",但父进程是 FPM worker,fork 出去的子进程不归 FPM 管理,出问题极难排查——把它当 CLI 专用工具。
适合场景:命令行批量任务、数据迁移、定时脚本分片。
第二步:Swoole——常驻内存的高并发运行时
安装(PECL 方式):
pecl install swoole
php --ri swoole | head -20
协程并发请求示例:
Swoole\Coroutine\run(function () {
for ($i = 0; $i < 5; $i++) {
go(function () use ($i) {
$cli = new Swoole\Coroutine\Http\Client('example.com', 443, true);
$cli->get('/');
echo $i, ':', strlen($cli->body), PHP_EOL;
});
}
});
关键点:进入协程环境后,所有 IO 必须换成协程版 API(`Swoole\Coroutine\System::sleep` 替代 `sleep`,协程版 HTTP/MySQL/Redis 客户端替代 `file_get_contents`、PDO)。用了阻塞函数,整个进程都会被卡住,协程调度直接失效。
注意:① Swoole 与 Xdebug 常冲突,开发环境和收产环境建议分开配置;② 常驻内存意味着全局变量、静态属性、容器单例会跨请求残留,容易造成数据串号,写代码时要假设"进程不会重启";③ 扩展对 PHP 版本很敏感,装之前先查官方版本对应表,别拿 PHP 8.5 硬怼老版本 Swoole。
适合场景:自建 HTTP/WebSocket 服务、长连接、高并发 API 网关。
第三步:Fibers——PHP 8.1 原生,但只是"半成品"
$fiber = new Fiber(function () {
$value = Fiber::suspend('first'); // 挂起,把控制权交回主流程
echo "收到: {$value}\n";
});
echo $fiber->start(), PHP_EOL; // 输出 first
$fiber->resume('hello'); // 输出 收到: hello
原生 Fiber 只提供 `start / resume / suspend / getReturn`,没有事件循环、没有非阻塞 IO、没有调度器。想用它做网络并发,必须搭配 Amp 或 ReactPHP 这类上层库(Amp v3 就是基于 Fiber 实现的)。
注意:`Fiber::suspend()` 只能在 Fiber 内部调用,在主流程里调会抛异常;Fiber 也不能跨进程/跨线程传递。它解决的是"把回调地狱改写成同步写法",不是"提升吞吐"。
适合场景:写不依赖扩展的异步库、在 FPM 环境里改造已有同步代码结构。
第四步:按场景选,别按热度选
- FPM 站点里想并发几个 HTTP 请求:用 `curl_multi_*`,别上 pcntl/Swoole,改动最小。
- CLI 批量任务、跑满多核:pcntl 多进程,简单可控。
- 要跑常驻服务、长连接、单机万级并发:Swoole(或 Workerman、RoadRunner 这类替代品)。
- PHP 8.1+,想把异步代码写得像同步,又不想锁死扩展:Fiber + Amp/ReactPHP。
- 只是想把耗时操作挪出请求:消息队列 + 后台消费进程,比上面任何方案都稳。
补一句现实约束:如果你维护的是像 Clara BBS 这类无框架轻量级论坛系统(PHP 7.4–8.5 + MySQL,无需 Composer、无需命令行),它整站跑在传统 FPM 模型上,这时候引入 Swoole 属于架构级改造,性价比很低;更划算的做法是把并发需求交给队列和计划任务。并发方案要和现有运行模型匹配,而不是越先进越好。
小结
- pcntl = 多进程,仅 CLI,Web 请求里禁用。
- Swoole = 扩展级协程运行时,必须全程使用协程版 IO,注意常驻内存的脏状态。
- Fibers = PHP 8.1+ 语言原语,本身不做并发,要配 Amp/ReactPHP。
- FPM 场景优先 curl_multi 或队列,别为了并发把整个运行模型换掉。
- 选型顺序:先看运行位置(CLI/常驻/FPM),再看是否允许装扩展,最后才看写法偏好。
转载请注明出处,版权归原作者所有。
正式会员
认证极客





