27

有没有办法让函数超时?我有 10 分钟的时间来完成一项工作。该作业包括一个 for 循环,这是一个示例:

<?php
foreach($arr as $key => $value){
   some_function($key, $value); //This function does SSH and SFTP stuff
}
?>

$arr 有 15 个元素, some_function() 有时可能需要超过 1 分钟。事实上,一旦它被绞死了 5 分钟。

有没有办法让函数调用超时并继续 $arr 中的下一个元素?

谢谢!!

4

7 回答 7

21

这取决于您的实施。PHP 中 99% 的函数都是阻塞的。意思是在当前功能完成之前,处理不会继续。但是,如果函数包含循环,您可以添加自己的代码以在满足特定条件后中断循环。

像这样的东西:

foreach ($array as $value) {
  perform_task($value);
}

function perform_task($value) {
  $start_time = time();

  while(true) {
    if ((time() - $start_time) > 300) {
      return false; // timeout, function took longer than 300 seconds
    }
    // Other processing
  }
}

无法中断处理的另一个示例:

foreach ($array as $value) {
  perform_task($value);
}

function perform_task($value) {
    // preg_replace is a blocking function
    // There's no way to break out of it after a certain amount of time.
    return preg_replace('/pattern/', 'replace', $value);
}
于 2012-05-14T16:27:49.207 回答
13

您想要的是使用 pcntl_alarm 函数,该函数将在超时时触发 SIGALRM 信号。

例如:

// 10 minutes
pcntl_alarm( 600 );
pcntl_signal(SIGALRM, function() { print( "Timed-out!\n" ); exit( 0 ); });

请参阅此处的 PHP 手册了解更多信息: http: //php.net/manual/en/function.pcntl-alarm.php

于 2015-05-27T02:46:43.457 回答
9

PHP 是单线程的......你必须使用你的操作系统的能力来分叉另一个进程。

我知道如何做到这一点的唯一方法是使用 exec 命令分叉另一个完整进程。把你想要计时的东西放在一个单独的脚本中,在脚本上调用 exec,然后立即休眠 10 分钟。如果在 10 分钟时进程仍在运行,请将其杀死。(您应该通过使命令在后台运行新的 php 代码,通过命令行获取它并从 exec 命令返回它来获得它的 PID)。

于 2012-05-14T16:28:16.260 回答
5

你可以尝试使用'curl'来实现它。Curl——来自 PHP——主要用于做 HTTP/FTP 的东西,但它支持更多的协议,包括 ssh/sftp。我从来没有使用 curl 做过与 ssh/sftp 相关的事情,所以我不能提供任何额外的建议,但你应该能够在 stackoverflow 或其他地方找到更多信息。

您可以使用一个选项“CURLOPT_TIMEOUT”来配置您的请求的超时时间(还有一个选项“CURLOPT_CONNECTTIMEOUT”)。您甚至可以以毫秒为单位指定超时 (CURLOPT_TIMEOUT_MS / CURLOPT_CONNECTTIMEOUT_MS)。

无论如何:对于一个 cronjob,我建议使用一个额外的“锁定文件”你的脚本在它启动时写入并在它完成运行时删除。您可以在脚本中检查此锁定文件,因此如果 cron在上次运行完成之前触发您的脚本,您可以直接退出脚本而无需做任何进一步的操作。这确保了您的脚本不会被并行执行多次。

您也可以在 PHP 文档中找到有关 CURL 选项和 CURL 本身的更多信息:

你必须确保你安装的 libcurl 和/或 curl 支持 SSH/SFTP,但是:

等等

希望有帮助...

于 2012-05-14T19:58:33.210 回答
4

在开始进入循环之前,设置一个记住time(). 在每次迭代结束时,检查电流time()是否大于 60 秒。如果是这样,break. 例如:

<?php
$startTime=time();
foreach($arr as $key => $value){
    some_function($key, $value); //This function does SSH and SFTP stuff
    if (time()-$startTime>60) break; //change 60 to the max time, in seconds.
}
?>
于 2012-05-14T16:29:00.803 回答
1

看看set_time_limit。现在函数本身并不是你想要的,因为它只考虑了执行时间,这意味着现实生活中的 5 秒可能仍然只有 0.2 秒的执行时间。但是查看链接的原因是为了评论。用户发布了几个解决方案。

于 2012-05-14T16:43:42.240 回答
0

对于那些在命令行中的人,看看 sleep 是如何被中断的:

echo date("H:i:s\n");
pcntl_signal(SIGALRM ,function($signal){
echo "arghhh! i got interrupted \n";
},true);

pcntl_alarm(3);
sleep(20);
pcntl_signal_dispatch();


echo date("H:i:s\n");

pcntl_alarm(3);
sleep(20);

echo date("H:i:s\n");

pcntl_signal_dispatch();
echo "end\n";
于 2018-12-28T23:28:05.813 回答