0

这是一个并行执行命令数组并返回结果数组的函数,可选地通过回调函数和进程数量进行过滤,它在我的 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 上串行运行。

提前感谢您的帮助,祝您好运!

4

0 回答 0