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

setalarm() not apparently calling my handler

我已经使用 Perl Threads 设置了一个多线程应用程序,并试图检测线程内的超时(线程应该经过几个可能超时的系统调用,我需要知道它何时停止)。

我试过使用 alarm() 并遇到了由 thread0 而不是触发警报的线程接收警报信号的有据可查的问题。所以我转而使用 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"。相反,他们都采取了退出路径。

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

根据the threads documentation

Signals are caught by the main thread (thread ID = 0) of a script. Therefore, setting up signal handlers in threads for purposes other than THREAD SIGNALLING as documented above will not accomplish what is intended.

This is especially true if trying to catch SIGALRM in a thread. To handle alarms in threads, set up a signal handler in the main thread, and then use THREAD SIGNALLING to relay the signal to the thread:

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