首页 > 解决方案 > setalarm() 显然没有调用我的处理程序

问题描述

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

我尝试使用 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

标签: perlalarm

解决方案


根据线程文档

信号被脚本的主线程(线程 ID = 0)捕获。因此,在线程中设置信号处理程序以用于上述线程信号以外的目的将无法实现预期的目的。

如果试图在线程中捕获 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

推荐阅读