setalarm()显然没有调用我的处理程序

时间:2019-07-09 15:54:47

标签: perl alarm

我已经使用Perl Threads建立了一个多线程应用程序,并且正在尝试检测线程内的超时(该线程应该经过几个系统调用,这些超时可能会超时,我需要知道它何时停止)。

我尝试使用alarm(),并遇到了线程0(而不是触发警报的线程)接收到的警报信号的详细记录问题。所以我转向使用Alarm :: Concurrent。

use threads ('yield',
             'stack_size' => 64*4096,
             'exit' => 'threads_only',
             'stringify');


use Alarm::Concurrent qw(setalarm clearalarm);

threads->create(\&MyThreadFunc,1);
threads->create(\&MyThreadFunc,1);
threads->create(\&MyThreadFunc,5);
threads->create(\&MyThreadFunc,5);

sub MyThreadFunc {

    my $result = "Never Ran";
    my $timeToSleep = $_[0];
    my $tid = threads->tid();
    eval {
            setalarm  3, sub { die "Timedout\n" };
            sleep ($timeToSleep);
            clearalarm;
            $result = "TID $tid EXITED\n";
    };
    if ($@ eq "Timedout\n")
    {
            $result = "TID $tid TIMED OUT\n";
    }
    elsif ($@)
    {
            $result = "$@";
    }
    print "Thread returning $result";
    return $result;
}

 while (scalar threads::list(threads::running))
 {
 foreach my $thread (threads->list(threads::joinable)) { $thread->join(); }
 }

我期望的是:
我希望线程TID 1和2正常退出,线程3和4退出TimedOut路径(因为将调用警报,并且eval()会捕获到“ Timedout \ n”。) EXITD路径。

Thread returning TID 1 EXITED
Thread returning TID 2 EXITED
Thread returning TID 4 EXITED
Thread returning TID 3 EXITED

1 个答案:

答案 0 :(得分:1)

根据the threads documentation

  

信号被脚本的主线程(线程ID = 0)捕获。   因此,出于其他目的在线程中设置信号处理程序   比上面记录的THREAD SIGNALING无法完成   

     

如果尝试在线程中捕获SIGALRM,则尤其如此。至   处理线程中的警报,在主线程中设置信号处理程序,   然后使用THREAD SIGNALING将信号中继到线程:

my $thr = threads->create(sub {
  threads->yield();
  eval {
      $SIG{ALRM} = sub { die("Timeout\n"); };
      alarm(10);
      ...  # Do work here
      alarm(0);
  };
  if ($@ =~ /Timeout/) {
      warn("Task in thread timed out\n");
  }   
};  

# Set signal handler to relay SIGALRM to thread   
$SIG{ALRM} = sub { $thr->kill('ALRM') };   ... # Main thread continues working

以下是如何使用它的示例:

use threads ('yield',
             'stack_size' => 64*4096,
             'exit' => 'threads_only',
             'stringify');
use feature qw(say);
use strict;
use warnings;
use Alarm::Concurrent qw(setalarm clearalarm);

{
    create_thread(sleep_time => 1, timeout => 3);
    create_thread(sleep_time => 1, timeout => 3);
    create_thread(sleep_time => 5, timeout => 3);
    create_thread(sleep_time => 5, timeout => 3);

    while (scalar threads::list(threads::running)) {
        foreach my $thread (threads->list(threads::joinable)) { $thread->join(); }
    }
}

sub create_thread {
    my (%options) = @_;

    my $thr = threads->create(\&MyThreadFunc, $options{sleep_time});
    setalarm $options{timeout}, sub {
        $thr->kill('SIGTERM');
    };
}

sub MyThreadFunc {
    my $result = "Never Ran";
    my $timeToSleep = $_[0];
    my $tid = threads->tid();
    eval {
        local $SIG{TERM} = sub { die "Timedout\n" };
        sleep ($timeToSleep);
        $result = "TID $tid EXITED\n";
    };
    if ($@ eq "Timedout\n") {
        $result = "TID $tid TIMED OUT\n";
    }
    elsif ($@) {
        $result = "$@";
    }
    print "Thread returning $result";
    return $result;
}

输出

Thread returning TID 1 EXITED
Thread returning TID 2 EXITED
Thread returning TID 3 TIMED OUT
Thread returning TID 4 TIMED OUT