PHP中的并发:线程、协程与async/await——含示例解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么PHP开发者需要考虑并发?

\n\n

曾几何时,人们认为PHP是一种用于“快速脚本”的语言,所有操作都是顺序执行,不需要任何严肃的并行处理。时代变了。如今,我们用PHP构建高负载API、任务队列、WebSocket甚至微服务。这就引出了一个问题:如何让PHP同时处理多项任务,而不阻塞一切?

\n\n

在本文中,我们将探讨PHP中的并发的三种方法:经典线程(pthreads)、轻量级协程(Fiber)以及现代的async/await语法。您将看到真实的代码示例,了解每种工具的适用场景,以及2025年究竟该如何选择。

\n\n

如果您正在寻找用于数据处理的PHP并行执行方案,或者想了解PHP协程与线程的区别,那么您来对地方了。

\n\n

1. PHP中的多线程:pthreads——老而弥坚

\n\n

这是什么,如何工作?

\n

pthreads是一个扩展,它让PHP能够创建真正的操作系统线程。每个线程都是一个独立的Zend Engine实例,拥有自己的内存,但可以通过Threaded对象交换数据。

\n\n

一个重要细节: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
\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,给其他任务机会                

博客

书籍推荐