3

编辑:我已经标记了这个 C,希望得到更多的回应。我感兴趣的更多的是理论而不是特定的语言实现。因此,如果您是 C 编码员,请将以下 PHP 视为伪代码,并随时用 C 编写的答案来回应。

我试图通过让它并行而不是串行执行其任务来加速 PHP CLI 脚本。这些任务彼此完全独立,因此它们开始/完成的顺序无关紧要。

这是原始脚本(请注意,为清楚起见,所有这些示例都被剥离):

<?php

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

foreach ($items as $item) {
    do_stuff_with($item);
}

我已经设法使它与如下所示$items并行工作:pcntl_fork()

<?php

ini_set('max_execution_time', 0); 
ini_set('max_input_time', 0); 
set_time_limit(0);

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

$pids = array();
foreach ($items as $item) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork()");
    } elseif ($pid > 0) {
        // parent
        $pids[] = $pid;
    } else {
        // child
        do_stuff_with($item);
        exit(0);
    }   
}

foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

现在我想扩展它,以便一次最多有 10 个孩子活跃。处理这个问题的最佳方法是什么?我尝试了几件事,但运气不佳。

4

4 回答 4

2

我能想到的最好的办法是将所有任务添加到队列中,启动所需的最大线程数,然后让每个线程从队列中请求任务,执行任务并请求下一个任务。当没有更多任务要做时,不要忘记让线程终止。

于 2008-12-03T05:51:40.700 回答
2

分叉是一项昂贵的操作。从外观上看,您真正想要的是多线程,而不是多处理。不同之处在于线程比进程轻得多,因为线程共享一个虚拟地址空间,但进程具有单独的虚拟地址空间。

我不是 PHP 开发人员,但快速的 Google 搜索显示 PHP 本身不支持多线程,但有一些库可以完成这项工作。

无论如何,一旦你弄清楚如何产生线程,你就应该弄清楚要产生多少线程。为了做到这一点,您需要知道您的应用程序的瓶颈是什么。瓶颈是 CPU、内存还是 I/O?您在评论中指出您是网络绑定的,并且网络是一种 I/O。

如果您受 CPU 限制,那么您将获得与 CPU 内核一样多的并行性;更多线程,您只是在浪费时间进行上下文切换。假设您可以计算出要生成的总线程数,您应该将您的工作分成这么多单元,并让每个线程独立处理一个单元。

如果您受内存限制,那么多线程将无济于事。

由于您受 I/O 限制,因此确定要生成多少线程有点棘手。如果所有工作项的处理时间大致相同且方差非常小,则可以通过测量一个工作项花费的时间来估计要生成的线程数。但是,由于网络数据包往往具有高度可变的延迟,因此不太可能出现这种情况。

一种选择是使用线程池 - 您创建一大堆线程,然后对于要处理的每个项目,您查看池中是否有空闲线程。如果有,则让该线程执行该工作,然后移至下一个项目。否则,您将等待线程可用。选择线程池的大小很重要——太大了,你在浪费时间做不必要的上下文切换。太少了,而且您太频繁地等待线程。

另一种选择是放弃多线程/多处理,而只做异步 I/O。既然您提到您正在使用单核处理器,这可能是最快的选择。您可以使用诸如socket_select()测试套接字是否有可用数据之类的功能。如果是,您可以读取数据,否则您将移动到不同的套接字。这需要做更多的簿记工作,但是当数据在另一个套接字上可用时,您可以避免等待数据进入一个套接字。

如果你想避开线程和异步 I/O 并坚持使用多处理,如果每项处理足够昂贵,它仍然是值得的。然后,您可以像这样进行工作划分:

$my_process_index = 0;
$pids = array();

// Fork off $max_procs processes
for($i = 0; $i < $max_procs - 1; $i++)
{
  $pid = pcntl_fork();
  if($pid == -1)
  {
    die("couldn't fork()");
  }
  elseif($pid > 0)
  {
    // parent
    $my_process_index++;
    $pids[] = $pid
  }
  else
  {
    // child
    break;
  }
}

// $my_process_index is now an integer in the range [0, $max_procs), unique among all the processes
// Each process will now process 1/$max_procs of the items
for($i = $my_process_index; $i < length($items); $i += $max_procs)
{
  do_stuff_with($items[$i]);
}

if($my_process_index != 0)
{
  exit(0);
}
于 2008-12-03T05:57:54.853 回答
2

没有系统调用来获取子 pid 的列表,但ps可以为您完成。

--ppidswitch 将列出您处理的所有子进程,因此您只需要计算ps.

或者,您可以维护自己的计数器,您将fork()在信号上递增和递减SIGCHLD,假设ppid对于分叉处理保持不变。

于 2008-12-03T08:09:00.300 回答
0

人 2 设定限制

这将是每个用户,这可能是你想要的。

于 2008-12-03T05:36:53.163 回答