PHP不支持真正多线程,实际可用的是pcntl_fork(CLI下进程复制)、已废弃的pthreads及推荐的parallel扩展(进程池+序列化),选型需据任务类型、运行环境和依赖条件综合判断。

PHP 本身不支持多线程,所谓“多线程编程”在 PHP 中其实是多种并发模型的混称,真正能用的方案取决于你的环境、任务类型和部署方式。别被“线程”二字带偏——你大概率要学的不是线程,而是如何让多个任务并行跑起来。
pcntl_fork 在 CLI 下模拟并发最实用
pcntl_fork 是 PHP 原生支持度最高、兼容性最好、也最容易上手的并发手段,但它不是线程,是进程复制。适合 CPU 密集型或需完全隔离内存的场景(比如批量处理图像、解析大文件)。
-
pcntl_fork()返回值决定执行分支:返回0是子进程,返回正整数是父进程中子进程 PID,返回-1表示失败 - 子进程必须以
exit()结束,否则会继续执行后续父进程逻辑,导致意外 fork 爆炸 - 父进程要用
pcntl_waitpid()或pcntl_wait()回收,否则子进程变成僵尸进程 - 不支持 Windows;Web SAPI(如 Apache、FPM)下基本不可用,仅限 CLI
<?php
$pid = pcntl_fork();
if ($pid === -1) {
die("fork failed");
} elseif ($pid === 0) {
// 子进程
echo "Child: start\n";
sleep(2);
echo "Child: done\n";
exit(0); // 必须 exit
} else {
// 父进程
pcntl_waitpid($pid, $status);
echo "Parent: child finished\n";
}
?>pthreads 扩展已废弃,别再踩坑
pthreads 曾经是唯一能写真线程的扩展,但它从 PHP 7.4 起彻底废弃,PHP 8+ 完全移除。现在装了也跑不起来,文档里还留着的示例代码全是历史遗迹。
- 依赖 ZTS 编译模式,而绝大多数生产环境(包括 Docker 官方镜像、Ubuntu/Debian 的 apt 包)默认是非 ZTS 的
- 即使强行编译 ZTS + pthreads,线程间共享对象需手动加锁(
Threaded、SyncMutex),极易写出竞态 bug - 社区早已转向
parallel扩展(见下条),但parallel也不是“线程”,而是进程池 + 序列化通信
结论:看到任何教程还在教 new Thread(),直接跳过。
立即学习“PHP免费学习笔记(深入)”;
parallel 扩展是目前最接近“安全多线程”的替代方案
parallel 是 PHP 官方推荐的现代并发扩展(PHP 7.2+,需单独安装),它用进程池 + 序列化参数/返回值的方式规避线程安全问题,比 pcntl 更易管理,比 pthreads 更可靠。
- 所有任务函数必须是纯函数(不能访问外部变量或全局状态),参数和返回值会被序列化传输
- 不支持闭包直接传入,需封装为具名函数或静态方法
- 进程间无共享内存,天然避免竞态,但也意味着无法实时通信或共享中间状态
<?php
use parallel\Runtime;
<p>$runtime = new Runtime();
$future = $runtime->run(function ($a, $b) {
return $a + $b;
}, [2, 3]);</p><p>var_dump($future->value()); // int(5)
?></p>注意:parallel 仍要求 CLI 模式,且不能在 FPM/Apache 中使用;Windows 支持有限(需 WSL 或 Cygwin)。
Swoole 协程不是多线程,但对 I/O 并发更高效
如果你的任务主要是 HTTP 请求、数据库查询、Redis 调用等 I/O 操作,Swoole 的协程方案比任何“多进程/多线程”都更轻量、更高吞吐。
- 协程是单线程内调度,没有进程/线程创建开销,上下文切换成本极低
- 所有 I/O 操作(
co::sleep、Coroutine\Http\Client、MySQL::query)需使用 Swoole 封装的协程版 API,原生curl或mysqli会阻塞整个协程 - 不需要
pcntl或 ZTS,安装简单,Web 和 CLI 都可运行
<?php
Swoole\Coroutine\run(function () {
go(function () {
$client = new Swoole\Coroutine\Http\Client('httpbin.org', 80);
$client->get('/delay/1');
echo "req1 done\n";
});
go(function () {
$client = new Swoole\Coroutine\Http\Client('httpbin.org', 80);
$client->get('/delay/1');
echo "req2 done\n";
});
});
?>关键点:协程不是并发的“银弹”——CPU 密集型任务(如大数组排序、加密计算)会阻塞整个事件循环,此时仍需 parallel 或子进程。
实际选型时,先问自己三个问题:
任务是 CPU 密集型还是 I/O 密集型?
运行环境是 CLI 还是 Web(FPM)?
是否允许引入 C 扩展(如 Swoole)或额外依赖(如 Redis 做队列)?
没想清楚就硬套“多线程”,最后卡在僵尸进程、序列化失败、协程阻塞或者扩展装不上,反而拖慢进度。



















