我同时为蜘蛛页面编写了一个网络蜘蛛。对于蜘蛛找到的每个链接,我想派生一个新的孩子重新开始这个过程。
我不想让目标服务器超载,所以我创建了一个所有对象都可以访问的静态数组。每个孩子都可以将他们的PID添加到数组中,父母或孩子都应该检查数组以查看是否满足$maxChildren,如果满足,请耐心等待任何孩子完成。
如您所见,我将 $maxChildren 设置为 3。我希望在任何给定时间看到 3 个同时进行的进程。然而,事实并非如此。linux top 命令在任何给定时间显示 12 到 30 个进程。在并发编程中,如何调节并发进程的数量?我的逻辑目前受到 Apache 如何处理它的最大子代的启发,但我不确定它是如何工作的。
正如其中一个答案所指出的,全局访问静态变量会带来竞争条件问题。为了解决这个问题,$children 数组将进程的唯一 $PID 作为键和它的值,从而创建一个唯一值。我的想法是,由于任何对象只能处理一个 $children[$pid] 值,因此不需要锁定。这不是真的吗?两个进程是否有可能在某个时候尝试取消设置或添加相同的值?
private static $children = array();
private $maxChildren = 3;
public function concurrentSpider($url) {
// STEP 1:
// Download the $url
$pageData = http_get($url, $ref = '');
if (!$this->checkIfSaved($url)) {
$this->save_link_to_db($url, $pageData);
}
// STEP 2:
// extract all hyperlinks from this url's page data
$linksOnThisPage = $this->harvest_links($url, $pageData);
// STEP 3:
// Check the links array from STEP 2 to see if this page has
// already been saved or is excluded because of any other
// logic from the excluded_link() function
$filteredLinks = $this->filterLinks($linksOnThisPage);
shuffle($filteredLinks);
// STEP 4: loop through each of the links and
// repeat the process
foreach ($filteredLinks as $filteredLink) {
$pid = pcntl_fork();
switch ($pid) {
case -1:
print "Could not fork!\n";
exit(1);
case 0:
if ($this->checkIfSaved($filteredLink)) {
exit();
}
//$pid = getmypid();
print "In child with PID: " . getmypid() . " processing $filteredLink \n";
$var[$pid]->concurrentSpider($filteredLink);
sleep(2);
exit(1);
default:
// Add an element to the children array
self::$children[$pid] = $pid;
// If the maximum number of children has been
// achieved, wait until one or more return
// before continuing.
while (count(self::$children) >= $this->maxChildren) {
//print count(self::$children) . " children \n";
$pid = pcntl_waitpid(-1, $status);
unset(self::$children[$pid]);
}
}
}
}
这是用 PHP 编写的。我知道pcntl_waitpid
参数为 -1 的函数等待任何孩子完成,而不管父母(http://php.net/manual/en/function.pcntl-waitpid.php)。
我的逻辑有什么问题,如何纠正它以便只有$maxChildren
进程同时运行?如果您有建议,我也愿意改进总体逻辑。