当代码部分';的执行时间超过了指定的时间限制


Is it possible to terminate a code part when it's execution time is getting longer than a specified time limit?

我目前正在CMS应用程序中的计划任务(如cronjobs(系统上工作。我想知道当一段代码的执行时间超过指定的时间限制时,是否有可能终止它。

有些计划任务可能很重,一个任务可能会运行到无限循环中,以保持其短暂性;任务占用大量时间的原因有很多。其中一个为数不多的问题是,该任务将占用比实际需要多得多的资源,这会降低其运行的服务器的性能,这会使CMS和网站本身的可靠性降低。

将系统想象如下:一旦调度任务管理器被触发,它将执行在该时间调度的所有任务。想象一个简单的for循环,它将一个接一个地同步执行每个任务。想想下面这个简单的例子:

// This array would contain an array with all scheduled tasks that should be executed.
$tasks = Array();
// Loop through each task
foreach($tasks as $t) {
    // Execute the task
    $t->execute();
}

现在,$task->execute();方法使PHP的解释器等待任务的代码执行完毕。我的问题是,如果execute();方法的执行时间超过指定时间,是否有任何可能的方法来终止它后面运行的代码。

例如,如果提供了10秒的时间限制,并且任务执行其代码的时间超过30秒,则任务的运行代码应在10秒后终止,以允许任务调度程序继续执行下一个任务。

可以通过一个简单的if语句在每个计划任务的代码中实现这一功能,该语句在每次执行代码的一部分时检查时间。这方面的问题是,这需要在每个任务的代码中实现,因为该系统旨在供第三方开发人员使用,以便正确使用这些类型的任务,并使用他们的插件来实现这些任务。我不喜欢"强迫"所有使用任务来实现这些功能的开发人员。

我知道PHP提供的set_time_limit();方法,问题是这个方法会终止整个会话,而不是那个方法,这就是为什么这不能解决问题。

"移植"我的评论到一个答案:

另一个问题也很相似。

PHP的默认实现是在单个线程中运行脚本,因此不可能从脚本本身取消代码执行
只有非常非常少数PHP函数支持异步操作,例如,您可以使用mysqlnd运行异步查询,但此特定功能是mysql驱动程序的一部分,而不是PHP。

有一些扩展将多线程引入PHP,即pthread
要推荐另一个来源,请阅读扩展作者的回答。

不幸的是,对于一个"主流"项目(它应该在尽可能通用的设置上运行得尽可能好(,使用pthreads是不可行的
该扩展不太可能安装在任何共享的Web空间上。

此外,请记住,线程安全编程(或脚本编写对于那些关心它的人来说(是一项艰巨的任务,你必须关心它。这意味着要准备好你的线程代码,以免锁定等待(死锁(、损坏彼此的数据等。
关于线程安全这一术语的详细答案可以在这里找到。

仔细考虑所涉及的工作、出错的风险以及使用线程的好处
确保你的愿望真的足够重要,可以引入复杂的线程

我遇到过这种问题,我有一个技巧可以解决这个问题。我的解决方法是,我使用cURL执行本地脚本,如下所示:

// This is the curl call which will execute the local script
$execute_script = curl_call('local_script.php');

在"local_script.php"文件中是执行的代码

// In local_script.php
// Set time limit
set_time_limit(10);
// Execute task
$task->execute();
// Print 1 to let the curl call know that the script executed at the right time
echo '1';

然后,在cURL调用部分,您可以检查脚本在返回"1"时是否执行良好

if ($execute_script == '1'){
  // Successful script
} else {
  // Timed out
}

希望对有所帮助

如果超过时间,您可以中断循环:

$max = 10; //maximum time in seconds
$start = microtime(true);
foreach($tasks as $t) {
    // Execute the task
    $t->execute();
    if (microtime(true) - $start > $max) {
        break;
    }
}