引言:为什么PHP中的并发并非神话
\n传统上,PHP被认为是一种同步语言,每个请求按顺序处理。然而,从视频处理到大数据解析等现代应用,都需要同时执行多个任务。没有并发,您可能会遇到“性能瓶颈”,即使强大的服务器也会在等待I/O操作完成时闲置。
\n在本文中,我们将深入探讨PHP中的高级并发技术:从系统调用到内核级扩展。您将学习如何让PHP并行工作,同时不失去对资源的控制。
\n\n1. 基础:PHP中的进程 vs 线程
\n在深入代码之前,理解PHP上下文中进程与线程的区别非常重要。
\n- \n
- 进程——独立的实例,每个拥有自己的内存。通过
pcntl_fork()创建。适用于重型任务,但需要进程间通信(IPC)。 \n - 线程——同一进程内的轻量级“子任务”,共享内存。通过
pthreads扩展实现。非常适合处理共享数据,但需要同步。 \n
在高级水平上,重要的是能够结合使用这两种方法。例如,使用fork隔离关键任务,而使用线程在同一个上下文中进行并行处理。
\n\n1.1 通过pcntl_fork()实现并行进程
\n系统调用pcntl_fork()创建一个子进程,复制父进程的内存。以下是并行处理多个URL的示例:
<?php\n$urls = ['https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3'];\n$pids = [];\n\nforeach ($urls as $url) {\n $pid = pcntl_fork();\n if ($pid == -1) {\n die('无法创建进程');\n } elseif ($pid) {\n // 父进程:保存PID\n $pids[] = $pid;\n } else {\n // 子进程:执行下载\n $data = file_get_contents($url);\n file_put_contents('/tmp/' . md5($url) . '.json', $data);\n exit(0); // 终止子进程\n }\n}\n\n// 等待所有进程完成\nforeach ($pids as $pid) {\n pcntl_waitpid($pid, $status);\n}\necho "所有URL已并行处理!";\n?>\n重要提示:fork()后,子进程会继承所有打开的文件描述符和数据库连接。请在子进程中显式关闭它们,以避免冲突。
\n\n2. 使用ReactPHP和Amp实现异步任务
\n对于注重响应性的Web应用,请使用事件驱动循环库。ReactPHP和Amp允许执行非阻塞操作,而无需创建单独的进程。
\n\n2.1 ReactPHP示例
\n通过Composer安装库:composer require react/react。以下是一个异步HTTP客户端,同时发送三个请求:
<?php\nrequire 'vendor/autoload.php';\n\nuse React\\EventLoop\\Factory;\nuse React\\HttpClient\\Client;\n\n$loop = Factory::create();\n$client = new Client($loop);\n\n$urls = ['http://example.com', 'http://example.org', 'http://example.net'];\n$promises = [];\n\nforeach ($urls as $url) {\n $request = $client->request('GET', $url);\n $request->on('response', function ($response) use ($url) {\n echo "来自{$url}的响应:" . $response->getCode() . PHP_EOL;\n });\n $request->end();\n}\n\n$loop->run();\n?>\nReactPHP创建一个单一的事件循环,无阻塞地处理所有请求。这对于API网关或微服务非常理想,因为响应速度至关重要。
\n\n3. 使用pthreads管理线程(ZTS构建)
\npthreads扩展是在PHP中实现真正多线程的唯一方式。
博客
书籍推荐
推荐阅读