检测PHP中的代码块的超时

Vir*_*dia 13 php timeout

如果在PHP中花费太长时间,有没有办法可以中止一段代码?也许是这样的:

//Set the max time to 2 seconds
$time = new TimeOut(2);
$time->startTime();

sleep(3)

$time->endTime();
if ($time->timeExpired()){
    echo 'This function took too long to execute and was aborted.';
} 
Run Code Online (Sandbox Code Playgroud)

它不一定完全像上面那样,但有没有任何本机PHP函数或类做这样的事情?

编辑:Ben Lee的答案pcnt_fork将是完美的解决方案,除了它不适用于Windows.有没有其他方法可以使用适用于Windows和Linux的PHP实现此目的,但不需要外部库?

编辑2:XzKto的解决方案可在某些情况下,但不是一致,我似乎无法捕获异常,不管我怎么努力.用例是检测单元测试的超时.如果测试超时,我想终止它,然后继续下一个测试.

Ben*_*Lee 11

您可以通过分叉进程,然后使用父进程监视子进程来执行此操作.pcntl_fork是一个分叉进程的方法,所以你在内存中有两个几乎相同的程序并行运行.唯一的区别是在一个进程中,父进程pcntl_fork返回一个正整数,它对应于子进程的进程id.而在另一个过程中,孩子pcntl_fork返回0.

这是一个例子:

$pid = pcntl_fork();
if ($pid == 0) {
    // this is the child process
} else {
    // this is the parent process, and we know the child process id is in $pid
}
Run Code Online (Sandbox Code Playgroud)

这是基本结构.下一步是添加进程到期.您的东西将在子进程中运行,父进程将仅负责监视子进程并对其进行计时.但是为了让一个进程(父进程)杀死另一个进程(子进程),需要有一个信号.信号是进程通信的方式,意味着"你应该立即结束"的信号是SIGKILL.您可以使用posix_kill发送此信号.所以父母应该等待2秒然后杀死孩子,如下:

$pid = pcntl_fork();
if ($pid == 0) {
    // this is the child process
    // run your potentially time-consuming method
} else {
    // this is the parent process, and we know the child process id is in $pid
    sleep(2); // wait 2 seconds
    posix_kill($pid, SIGKILL); // then kill the child
}
Run Code Online (Sandbox Code Playgroud)

  • +1 - 这将是一个很好的解决方案,因为它很简单,但不幸的是,我需要一些适用于Windows和Linux的东西(参见注释:http://ca.php.net/manual/en/intro.pcntl. PHP).知道其他任何选择吗? (2认同)

XzK*_*Kto 8

如果你在一个命令(例如sleep())上编写脚本暂停,除了分叉之外你不能真的这样做,但是对于特殊情况有很多解决方法:如果你编程暂停数据库查询就像异步查询一样,proc_open如果你程序暂停一些外部执行等.不幸的是它们都不同,所以没有通用的解决方案.

如果脚本等待一个很长的循环/多行代码,你可以做一个像这样的脏技巧:

declare(ticks=1);

class Timouter {

    private static $start_time = false,
    $timeout;

    public static function start($timeout) {
        self::$start_time = microtime(true);
        self::$timeout = (float) $timeout;
        register_tick_function(array('Timouter', 'tick'));
    }

    public static function end() {
        unregister_tick_function(array('Timouter', 'tick'));
    }

    public static function tick() {
        if ((microtime(true) - self::$start_time) > self::$timeout)
            throw new Exception;
    }

}

//Main code
try {
    //Start timeout
    Timouter::start(3);

    //Some long code to execute that you want to set timeout for.
    while (1);
} catch (Exception $e) {
    Timouter::end();
    echo "Timeouted!";
}
Run Code Online (Sandbox Code Playgroud)

但我认为这不是很好.如果您指定确切的情况,我认为我们可以帮助您更好.


xda*_*azz 0

如果您不在安全模式下,设置时间限制怎么样?