如何限制在Perl脚本的特定部分中花费的时间?

YoD*_*Dar 7 perl timeout

有没有办法建立一个时间计数器,使脚本的部分运行只要它滴答?例如,我有以下代码:

for my $i (0 .. $QUOTA-1) {
    build_dyna_file($i);
    comp_simu_exe;
    bin2txt2errormap($i);
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,我想运行这个循环3分钟,即使循环指令尚未完成,它仍应在3分钟后突破循环.

实际上程序会打开一个时间计数器窗口,它与脚本的一部分并行工作(每次调用它).

另外,子调用'comp_simu_exe'运行外部模拟器(在shell中),当超时结束时 - 此过程也必须被杀死(不要假设在一段时间后返回).

sub comp_simu_exe{

system("simulator --shell");
}
Run Code Online (Sandbox Code Playgroud)

系统函数调用之间是否有任何关联?

inn*_*naM 12

您可以设置一个警报,该警报将在指定的秒数后突破您的代码:

eval {
    local $SIG{ ALRM } = sub { die "TIMEOUT" };
    alarm 3 * 60;
    for (my $i = 0 ; $i <$QUOTA ; $i++) {
        build_dyna_file($i);
        comp_simu_exe;
        bin2txt2errormap($i);
    }
    alarm 0;
};

if ( $@ && $@ =~ m/TIMEOUT/ ) {
    warn "operation timed out";
}
else {
    # somebody else died
    alarm 0;
    die $@;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果你真的需要循环运行至少三次,无论这需要多长时间:

eval {
    my $t0 = time;
    local $SIG{ ALRM } = sub { die "TIMEOUT" };

    for (my $i = 0 ; $i <$QUOTA ; $i++) {
        build_dyna_file($i);
        comp_simu_exe;
        bin2txt2errormap($i);
        if ( $i == 3 ) {
            my $time_remaining = 3 * 60 - time - $t0;
            alarm $time_remaining if $time_remaining > 0;
        }
    }
    alarm 0;
};
Run Code Online (Sandbox Code Playgroud)

  • 记得要运行`alarm 0;`如果`$ @`不匹配`'TIMEOUT'`.代码可能会在块eval内部死掉,并且`alarm 0;`内部将永远不会运行.这将导致虚假的'die`s'消息"闹钟\n". (4认同)
  • 最好使用示波器防护来执行"报警0",这样代码就不会重复了."使用Guard; eval {my $ guard = scope_guard {alarm 0}; <code>}"现在无论eval块如何退出,您都会重置警报. (4认同)

inn*_*naM 5

这是第二个答案,它涉及超时第二个过程的情况.使用这种情况启动外部程序并确保它不会花太长时间:

my $timeout = 180;
my $pid = fork;

if ( defined $pid ) {
    if ( $pid ) {
        # this is the parent process
        local $SIG{ALRM} = sub { die "TIMEOUT" };
        alarm 180;
        # wait until child returns or timeout occurs
        eval {
            waitpid( $pid, 0 );
        };
        alarm 0;

        if ( $@ && $@ =~ m/TIMEOUT/ ) {
            # timeout, kill the child process
            kill 9, $pid;
        }
    }
    else {
        # this is the child process
        # this call will never return. Note the use of exec instead of system
        exec "simulator --shell";
    }
}
else {
    die "Could not fork.";
}
Run Code Online (Sandbox Code Playgroud)