可以使用Cron作业来模拟PHP的多线程吗?

时间:2010-02-15 00:25:41

标签: php multithreading cron

我有一个填充了1000多条记录的mysql数据库表,可以说5000条记录。每条记录都有一个processed布尔标志,默认为false (0)。我想做的是每分钟在cron上运行一个PHP脚本。它的代码是这样的:

<?php
process();

function process()
{
   $sql = "SELECT id FROM items WHERE processed = '0' ORDER BY id ASC LIMIT 1";
   $result = $this->db->query($sql);

   if (! $result->has_rows())
     die;

   $id = $result->getSingle('id');
   processItem($id); //Will set processed to 1 after processing is done
   process();
}
?>

上面的代码应该很清楚,它获取未处理的下一条记录的id,处理它,然后再次调用process()函数,重复此过程,直到没有更多要处理的项目,此时执行将停止。

通过将此脚本放在Cron上每分钟运行一次,我希望此脚本的多个实例都能同时处理项目,因此,不是一次处理一个项目,而是可以处理5-10个项目同时进行。

1)这是否按照我计划的方式运作?有任何改进建议/需要注意的事项吗?

2)我是否应该让脚本为运行实例的数量设置一个计数器,因此每当cron作业启动时,它都会检查计数器,如果50(?)实例正在运行,它将退出而不进行处理。这可能会导致服务器因过多的运行进程占用太多内存而导致崩溃?有什么想法吗?

3 个答案:

答案 0 :(得分:7)

我有几件事要说:

首先,您使用递归来处理多行。如果你过于沉重,这可能会导致问题。而是使用一个简单的循环。

其次,您知道此代码是否可以从多次运行中受益?如果机器受CPU限制,则可能无法从另一个线程中受益。我建议你手动检查多少线程效果最好。更多的线程并不总是让事情变得更快,在某些情况下实际上可以减慢一切。

最后,我肯定会限制这些脚本可以同时运行的数量。这可以通过确保每个脚本运行不超过5分钟来实现。或者您可以保留活动脚本的数量,并确保它不超过您在我的第二个建议中确定的最大数量。

编辑:我添加了一些有关问题递归可能导致的更多信息: 每次递归调用函数时,堆栈上都会使用额外的空间。此空间存储任何局部变量以及函数的地址(允许它在被调用函数退出时恢复状态)。堆栈只有一个有限的空间,所以最终你的程序会因堆栈溢出而崩溃。尝试运行这个简单的程序:

function a($i) { 
   print $i . "\n"; 
   a($i + 1);
}
a(0);

在我的系统上,它在608739次迭代后崩溃了PHP。在更复杂的功能中,这个数字可能要小得多。一个简单的循环没有这些开销,因此没有这个问题。

答案 1 :(得分:1)

这种递归似乎根本没有必要,就像布兰普所说的那样,可能会导致问题。为什么不呢

$sql = "SELECT id FROM items WHERE processed = '0' ORDER BY id ASC LIMIT 1";

while ( ($result = $this->db->query($sql) && $result->has_rows() ) {
   processItem( $result->getSingle('id') );
}

然而,我预见到更大的问题。如果您将每分钟运行此脚本,您有什么机制来停止执行可能仍在运行的先前执行的脚本?您最终可能会多次处理相同的ID。

如果您绝对需要(伪)多线程方法,我建议如下:

  1. 获取一系列或所有未经处理的ID,而不是一次只获取一个。
  2. 使用curl_multi_系列函数,将上述结果的子集(n id的组)传递给另一个脚本以进行实际处理。
  3. 此方法允许您更好地控制整个过程,并防止不必要的单一查询获取未处理的ID。

答案 2 :(得分:0)

我开始了一个项目来解决完全相同的问题。如果需求量很大,它可以连续运行脚本,并行运行更多实例。如果没有任何操作,那么它将在运行脚本实例之前等待指定的时间间隔。

如果您有兴趣,请阅读一些使用案例:www.4pmp.com/fatcontroller/