浏览文章
文章信息
PHP 使用 proc_open() 生成非阻塞进程
402
例子#PHP 不支持并发运行代码,除非您安装了
pthread
.proc_open()
有时可以通过使用和并异步读取其输出来绕过此问题stream_set_blocking()
。如果我们将代码分成更小的块,我们可以将其作为多个子进程运行。然后使用
stream_set_blocking()
函数我们可以使每个子进程也成为非阻塞的。这意味着我们可以生成多个子进程,然后在循环中检查它们的输出(类似于偶数循环)并等待所有子进程完成。作为一个例子,我们可以有一个小的子进程,它只运行一个循环,并且在每次迭代中随机休眠 100 - 1000 毫秒(注意,一个子进程的延迟始终相同)。
<?php // subprocess.php $name = $argv[1]; $delay = rand(1, 10) * 100; printf("$name delay: ${delay}ms\n"); for ($i = 0; $i < 5; $i++) { usleep($delay * 1000); printf("$name: $i\n"); }
然后主进程将生成子进程并读取它们的输出。我们可以将它分成更小的块:
- 使用proc_open()生成子进程。
- 使用 使每个子进程非阻塞
stream_set_blocking()
。- 运行循环直到所有子进程完成使用
proc_get_status()
。- 使用每个子进程的输出管道正确关闭文件句柄
fclose()
,并使用 关闭进程句柄proc_close()
。<?php // non-blocking-proc_open.php // File descriptors for each subprocess. $descriptors = [ 0 => ['pipe', 'r'], // stdin 1 => ['pipe', 'w'], // stdout ]; $pipes = []; $processes = []; foreach (range(1, 3) as $i) { // Spawn a subprocess. $proc = proc_open('php subprocess.php proc' . $i, $descriptors, $procPipes); $processes[$i] = $proc; // Make the subprocess non-blocking (only output pipe). stream_set_blocking($procPipes[1], 0); $pipes[$i] = $procPipes; } // Run in a loop until all subprocesses finish. while (array_filter($processes, function($proc) { return proc_get_status($proc)['running']; })) { foreach (range(1, 3) as $i) { usleep(10 * 1000); // 100ms // Read all available output (unread output is buffered). $str = fread($pipes[$i][1], 1024); if ($str) { printf($str); } } } // Close all pipes and processes. foreach (range(1, 3) as $i) { fclose($pipes[$i][1]); proc_close($processes[$i]); }然后,输出包含来自所有三个子进程的混合物,因为我们通过fread()读取它们(注意,在这种情况下,
proc1
比其他两个子进程结束得早得多):$ php non-blocking-proc_open.php proc1 delay: 200ms proc2 delay: 1000ms proc3 delay: 800ms proc1: 0 proc1: 1 proc1: 2 proc1: 3 proc3: 0 proc1: 4 proc2: 0 proc3: 1 proc2: 1 proc3: 2 proc2: 2 proc3: 3 proc2: 3 proc3: 4 proc2: 4