【问题标题】:setalarm() not apparently calling my handlersetalarm() 显然没有调用我的处理程序
【发布时间】:2019-11-19 05:44:27
【问题描述】:

我已经使用 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

【问题讨论】:

  • 另见Perl threads with alarm 并根据Threads and ALARM() - There is a solution or workaround? alarm 使用信号(发送到进程而不是线程),因此它不适用于线程
  • 如果您的用例有more suitable option,当然,另一种选择是避免使用线程。
  • 对 cme​​ts 和答案假设的明确声明:我认为这个模块不能跨线程工作;查看源代码——它只记录(调度)“正常”警报。其名称中的“并发”是指它能够在同一个程序中拥有多个警报(我们不能使用内置的alarm)。要么使用解决方法(如在 Håkon 的回答中),要么使用其他方式来保持时间或其他工具(如分叉、事件)。
  • (上面的“同一程序中的多个警报”是指同时,“同时”)

标签: perl alarm


【解决方案1】:

the threads documentation

信号被脚本的主线程(线程 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

【讨论】:

  • 谢谢!看起来我误解了 Alarm::Concurrent 的功能。我原以为它应该在线程中分配,它会处理警报的“转发”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-03
  • 2019-09-26
相关资源
最近更新 更多