这是一个并行执行命令数组并返回结果数组的函数,可选地通过回调函数和进程数量进行过滤,它在我的 Mac 上的 Lion 上运行良好:
// executes commands in parallel, limited by the number of file descriptors or maxProcesses.
// pass an optional callback to filter each result (return a non-null value to stop execution).
function execParallel($commands, $callback = null, $maxProcesses = PHP_INT_MAX)
{
$handles = array();
$results = array();
for($i = 0; $i < count($commands) || count($handles);)
{
if($i < count($commands) && count($handles) < $maxProcesses)
if(($handle = @popen($commands[$i].' 2> /dev/null &', "r")) !== false)
{
stream_set_blocking($handle, 0); // does nothing on Windows
$handles[$i] = $handle;
$results[$i] = '';
$i++;
}
foreach($handles as $key => $handle)
{
$results[$key] .= fread($handle, 4096);
if(feof($handle))
{
pclose($handles[$key]);
unset($handles[$key]);
if($callback)
if($callback($results[$key], $key))
break;
}
}
}
foreach($handles as $key => $handle) // clear any incomplete tasks
{
pclose($handles[$key]);
unset($handles[$key]);
unset($results[$key]);
}
return $results;
}
这是一个使用匿名函数调用它的示例,并且在 php 5.3 中限制为 64 个线程:
$commands = array_fill(0, 256, 'sleep 1; echo | date');
$results = execParallel($commands, function($result, $key)
{
echo "$key\n";
if($key == 128) return false;
}, 64);
print_r($results);
输出是一个最多 128 个数字的序列,然后是大约 128 个时间戳,具体取决于时钟精度以及其他一些进程是否完成。如果出现问题,过滤器可以方便地在中途退出。
您可以尝试将进程限制从 64 更改为 1 以查看连续到达的结果,或者注释掉 $key == 128 行以防止停止。如果您没有设置进程数限制,它会尽可能多地打开进程的文件句柄,并等待更多的文件句柄在内部可用。
它的工作方式是,它几乎与 shell_exec() 相同,但在后台启动进程并继续执行。如果您通过关闭句柄过早地关闭进程,它将有关损坏管道的任何警告传递给 null。
我的问题是,我需要让它跨平台,但不能轻松访问 PC 或 Windows 的所有风格,如 NT。这是我的 TODO 清单:
- fread() 在 Windows 上阻塞,因为 stream_set_blocking() 是 NOP。也许可以用 fread(1) 解决这个问题,但它只是伪抢先的。需要一种方法来查看是否有字节在等待,可能是通过选择或以某种不同的方式使流非阻塞。
- 在后台生成进程,可能使用“START /B [command]”。这确实需要与 unix 中的“&”类似地工作,并且不生成额外的输出,阻止粗壮或强制它只能通过文件访问,强制用户创建批处理文件或其他奇怪的东西。它可能需要使用 proc_ open() 或 pcntl_fork() 或完全不同的东西,我不太确定。
- 探索使用 curl_multi 或其他线程工具来生成函数并像往常一样在其中调用 shell_exec() (这需要以不涉及安装任何额外库的方式完成)。
- 使用像 DIRECTORY_SEPARATOR == '\' 这样的良好测试在 Windows 上运行单独的代码分支。
- 在 Windows 上需要一些更好的测试用例,可能是 "TIMEOUT [seconds]" 或 "PING -n [milliseconds] 127.0.0.1>nul" 没有输出。
这一切都开始了,因为我试图在 php 中模拟goroutine之类的东西,您可以在其中生成最多 N 个进程,如果达到限制,它只会阻塞,直到有更多可用,然后返回结果。我一直需要加速应该很容易并行化的大型任务,但是 php 使这变得困难,因为由于一堆特定于操作系统的细节,他们的手被束缚了。
所以我认为无论谁想出了这个函数的可靠跨平台版本,或者非常类似于“正常工作”的 goroutine 的东西,都会发现他们的代码非常受欢迎。我的猜测是这个函数现在回退到在 Windows 上串行运行。
提前感谢您的帮助,祝您好运!