引言:为什么PHP开发者需要考虑并发?
\n\n曾几何时,人们认为PHP是一种用于“快速脚本”的语言,所有操作都是顺序执行,不需要任何严肃的并行处理。时代变了。如今,我们用PHP构建高负载API、任务队列、WebSocket甚至微服务。这就引出了一个问题:如何让PHP同时处理多项任务,而不阻塞一切?
\n\n在本文中,我们将探讨PHP中的并发的三种方法:经典线程(pthreads)、轻量级协程(Fiber)以及现代的async/await语法。您将看到真实的代码示例,了解每种工具的适用场景,以及2025年究竟该如何选择。
\n\n如果您正在寻找用于数据处理的PHP并行执行方案,或者想了解PHP协程与线程的区别,那么您来对地方了。
\n\n1. PHP中的多线程:pthreads——老而弥坚
\n\n这是什么,如何工作?
\npthreads是一个扩展,它让PHP能够创建真正的操作系统线程。每个线程都是一个独立的Zend Engine实例,拥有自己的内存,但可以通过Threaded对象交换数据。
一个重要细节:pthreads仅在CLI模式下工作,不支持Web环境(Apache/Nginx mod_php)。但这是获得CPU级别真正并行(而不仅仅是并发)的唯一途径。
\n\n示例:并行解析URL
\n<?php\n// 确保已安装pthreads扩展\nclass UrlFetcher extends Thread {\n private $url;\n private $result;\n\n public function __construct(string $url) {\n $this->url = $url;\n }\n\n public function run() {\n // 在单独的线程中执行\n $this->result = file_get_contents($this->url);\n }\n\n public function getResult() {\n return $this->result;\n }\n}\n\n$urls = ['https://api.example.com/1', 'https://api.example.com/2', 'https://api.example.com/3'];\n$threads = [];\n\nforeach ($urls as $url) {\n $thread = new UrlFetcher($url);\n $thread->start(); // 启动线程\n $threads[] = $thread;\n}\n\n// 收集结果\nforeach ($threads as $thread) {\n $thread->join(); // 等待完成\n echo $thread->getResult() . PHP_EOL;\n}\n?>\n\n这段代码启动了三个线程,每个线程下载各自的URL。执行时间大约等于最慢请求的时间,而不是三个请求的总和。这是PHP并行执行的一个绝佳示例。
\n\n何时使用pthreads?
\n- \n
- CPU密集型任务(图像处理、加密、PDF渲染)。 \n
- 在CLI脚本中解析大量数据。 \n
- 当需要真正的并行性,而非协作式多任务时。 \n
2. PHP中的协程:Fiber——轻量与可控
\n\n什么是Fiber?
\n从PHP 8.1开始,语言中引入了Fiber——类似于Go中的协程或Python中的协程。与线程不同,Fiber不会创建独立的操作系统进程。它是一种轻量级的执行单元,在单个线程中运行,并由开发者控制切换。
\n\n核心理念是:您自己决定何时暂停当前Fiber并将控制权交给另一个。这被称为协作式多任务。非常适合I/O操作(API请求、文件读取、数据库操作)。
\n\n示例:异步遍历目录
\n<?php\nfunction readFileAsync(string $path): Fiber {\n return new Fiber(function () use ($path) {\n $handle = fopen($path, 'r');\n $content = '';\n while (!feof($handle)) {\n $chunk = fread($handle, 4096);\n $content .= $chunk;\n // 暂停Fiber,给其他任务机会