PHP 并行 ≠ 并发。
这是两个在系统编程中严格区分的概念,混淆会导致对 PHP 能力边界和架构设计的根本误判。
一、定义(操作系统层面)
| 术语 | 定义 | 关键特征 |
|---|---|---|
| 并发(Concurrency) | 多个任务在同一时间段内交替执行(逻辑上“同时”) | - 单核 CPU 即可实现 - 通过时间片轮转、I/O 多路复用等实现 - 任务可能串行执行,但宏观上重叠 |
| 并行(Parallelism) | 多个任务在同一时刻真正同时执行 | - 需多核 CPU - 物理上同时运行 - 真正的“同时” |
✅类比:
- 并发= 一个人同时炒三道菜(切菜 → 热锅 → 翻炒,来回切换)
- 并行= 三个人各炒一道菜(真正同时进行)
二、PHP 的能力边界
1.PHP 原生不支持多线程
- Zend Engine 是单线程的:每个 PHP 进程(如 FPM Worker)一次只执行一个请求。
- 无共享内存模型:不同 PHP 进程间无法直接共享变量(需 Redis/DB 等外部介质)。
2.PHP 如何实现“并发”?
- 多进程模型(如 PHP-FPM):
- 启动 N 个 Worker 进程
- 每个进程处理一个请求 →宏观并发
- 但每个进程内部仍是串行执行
- 异步 I/O(非阻塞):
- 使用
swoole、ReactPHP、Amp等扩展 - 单进程内通过事件循环处理多个 I/O 任务(如并发 HTTP 请求)
- 仍非并行(CPU 密集型任务会阻塞整个事件循环)
- 使用
3.PHP 能否实现“并行”?
- 可以,但需多进程:
pcntl_fork()(仅 CLI):创建子进程,利用多核parallel扩展(PHP 7.2+):提供线程 API(但底层仍是多进程模拟)- Swoole Process Pool:管理多进程并行执行
- 限制:
- Web 环境(FPM)禁止
pcntl(安全原因) - 真正的多线程(共享内存)在 PHP 中几乎不可用(
pthreads已废弃)
- Web 环境(FPM)禁止
✅结论:
- Web 场景(FPM):只有多进程并发,无并行
- CLI 场景:可通过
fork实现多进程并行
三、典型场景对比
| 场景 | 模型 | 是并发? | 是并行? |
|---|---|---|---|
| Nginx + PHP-FPM(10 Worker) | 多进程 | ✅ | ✅(若 CPU ≥ 2 核) |
| Swoole HTTP Server(1 Worker) | 单进程事件循环 | ✅(I/O 并发) | ❌(CPU 任务串行) |
pcntl_fork()启动 4 子进程 | 多进程 | ✅ | ✅(多核下) |
| 普通 for 循环发 10 个 cURL | 同步阻塞 | ❌ | ❌ |
⚠️关键点:
- 多进程在多核 CPU 上 = 并发 + 并行
- 单进程事件循环 = 并发(仅 I/O),非并行
四、为什么这个区分重要?
1.性能优化方向不同
- 并发瓶颈(I/O 密集):
→ 用 Swoole/协程减少进程数,提升吞吐 - 并行瓶颈(CPU 密集):
→ 必须用多进程(如队列分片 +pcntl)
2.资源消耗模型不同
- 并发(事件循环):内存低,连接数高
- 并行(多进程):内存高(每进程独立),受
pm.max_children限制
3.编程模型不同
- 并发:回调、Promise、Generator(异步风格)
- 并行:进程间通信(IPC)、任务分发(如 Laravel Queue)
五、工程实践建议
| 需求 | 推荐方案 |
|---|---|
| Web API 高并发(I/O 密集) | Swoole / RoadRunner(协程并发) |
| 批量数据处理(CPU 密集) | CLI +pcntl_fork()或 Laravel Queue(多进程并行) |
| 混合场景 | Web 层用并发(Swoole),后台任务用并行(Queue) |
💡核心原则:
- 不要试图在 FPM 中做 CPU 并行(会阻塞 Worker)
- I/O 并发用协程,CPU 并行用多进程
总结
- 并发 = 逻辑上的同时(PHP 通过多进程或事件循环实现)
- 并行 = 物理上的同时(PHP 仅通过多进程在多核上实现)
- PHP 在 Web 环境(FPM)只有并发,无并行;
在 CLI 环境可通过多进程实现并行。 - 真正的工程能力:根据任务类型(I/O vs CPU)选择正确的模型,而非盲目追求“并行”。