PHP原生无多线程,pcntl_fork是CLI下可控并发起点,需exit()终止子进程、pcntl_waitpid()回收防僵尸;Swoole协程须CLI运行且I/O全用协程版;parallel扩展仅限PHP 8.1+ ZTS CLI环境;消息队列用于解耦而非语言层并发。

PHP 本身没有原生多线程,所谓“并发处理”必须靠扩展或架构设计来模拟或实现;直接写 thread_start() 或 new Thread() 会报错或静默失败。
pcntl_fork 是最可控的并发起点,但只跑在 CLI 下
Linux/macOS 环境下,pcntl_fork() 是你第一个该掌握的并发原语。它不依赖额外扩展(PHP 编译时默认启用),逻辑清晰:一次调用,父子进程分叉执行。
- 子进程必须以
exit()结束,否则会继续执行父进程后续代码,造成重复任务或资源泄漏 - 父进程要用
pcntl_waitpid()或pcntl_wait()回收子进程,否则留下僵尸进程 -
pcntl_fork()在 Windows 完全不可用,WAMP/XAMPP 环境下直接返回 -1 - 进程间无共享内存,传数据只能靠文件、信号量、消息队列或 Redis 这类外部介质
Swoole 协程是当前最实用的高并发路径,但 FPM 下完全无效
别被“协程”二字迷惑——它不是语言特性,而是 Swoole 扩展提供的运行时环境。你在 Nginx + PHP-FPM 下调用 Swoole\Coroutine\run(),只会得到 Fatal error: Uncaught Swoole\Error: No coroutines running。
- 必须用
php your_script.php启动 CLI 脚本,且脚本里第一行就得是Swoole\Coroutine\run(...) - 所有 I/O 操作必须用 Swoole 的协程版:比如
Swoole\Coroutine\Http\Client,不能混用curl_exec()或file_get_contents() -
go()和Swoole\Coroutine\create()等价,但每个协程里要独立 new 客户端实例,复用会导致状态混乱 - 超时必须显式设:
$client->set(['timeout' => 5]),否则默认为 0(永不超时),容易卡死整个协程调度器
parallel 扩展替代 pthreads,但仅限 PHP 8.1+
pthreads 已废弃多年,PHP 官方推荐的替代是 parallel 扩展。它更轻量、更安全,但限制也明确:
立即学习“PHP免费学习笔记(深入)”;
- 只支持 PHP 8.1 及以上版本,且必须启用 ZTS(Zend Thread Safety)模式
- 不能在 Web SAPI(如 FPM、Apache)中加载,只允许 CLI 模式下
dl('parallel.so')或配置extension=parallel - 任务函数不能引用闭包外的变量,所有数据需通过参数传递或序列化后传入
- 错误信息是
parallel\RuntimeError,不是普通 Exception,try/catch要捕获具体类型
消息队列不是“并发方案”,而是解耦手段
很多人把 Redis lPush() + 多个 worker 进程当成“PHP 并发”,其实它解决的是任务分发和削峰,不是语言层并发。关键点在于:
- 生产者和消费者完全解耦,worker 进程可横向扩容,也可用不同语言重写
- Redis 本身不是可靠队列,没 ACK 机制,worker 崩溃可能导致任务丢失;要用
BRPOPLPUSH+LREM手动实现半可靠消费 - Beanstalkd 或 RabbitMQ 更适合生产,但引入了运维复杂度——并发能力上限不再由 PHP 决定,而由中间件吞吐和网络延迟决定
- 别在同一个 PHP-FPM 请求里边 push 边 wait,那只是伪并发,主线程依然阻塞
真正卡住多数人的不是语法,而是环境边界:CLI 还是 FPM、ZTS 是否启用、扩展是否加载成功、I/O 是否被协程接管——这些条件缺一不可,漏一个就退化成串行执行。



















