【问题标题】:Signal handler not found未找到信号处理程序
【发布时间】:2017-12-13 21:57:21
【问题描述】:

由于某种原因,线程似乎无法找到 $SIG{'KILL') 信号。我从http://www.perlmonks.org/index.pl?node_id=557219这里的评论中得到代码,这是在 Windows 上。

use strict;
use warnings;

use threads;

sub test_sub{
    $SIG{'KILL'} = sub { threads->exit(); };
    while(1){}  
}

my $new_thread = threads->create('test_sub');

$new_thread->kill('KILL')->detach();

返回

Signal KILL received in thread 1, but no signal handler set. at test1.pl line 13.
Perl exited with active threads:
        1 running and unjoined
        0 finished and unjoined
        0 running and detached

编辑:看起来我正在使用线程版本“1.43”。我不知道是不是这个问题,但我会尝试更新的版本。

【问题讨论】:

  • 信号被传递给进程,我认为它只是一个全球性的事情。您需要将其隐藏在线程中吗?您可以使用在处理程序中设置的(全局)变量,但我看不出这将如何帮助单个线程。
  • 嗯。我猜不是,但是如果我有一个不同的线程想要对 KILL 信号进行一些清理呢?
  • 谢谢,这就是我要找的。​​span>
  • 嗯,您的代码在文档中,我没有意识到这一点。我可以在 v5.16 上确认行为。

标签: perl


【解决方案1】:

可能导致您观察到的行为的竞争条件不止一种,而是两种:

  • 您在孩子有机会设置信号处理程序之前发送 KILL 信号。

    下面,我使用同步来确保在发送信号之前创建信号处理程序。

  • 您在信号处理程序有机会调用threads->exit 之前退出程序。

    为什么人们坚持使用->detach

固定:

use strict;
use warnings;
use feature qw( say );

use threads;
use threads::shared;

sub test_handler {
    say sprintf "[%s %s] Starting thread", time, threads->tid;
    say sprintf "[%s %s] Doing stuff", time, threads->tid;
    sleep 4;
    say sprintf "[%s %s] Done doing stuff", time, threads->tid;
    say sprintf "[%s %s] Exiting thread", time, threads->tid;
}

sub create_thread(&) {
   my ($thread_func) = @_;

   # Reap any threads that might have exited.
   $_->join() for threads->list(threads::joinable);

   my $ready :shared = 0;
   my $thread = async {
      $SIG{KILL} = sub {
         say sprintf "[%s %s] Forcibly exiting thread", time, threads->tid;
         threads->exit();
      };

      # Signal creator that the thread is initialized.
      { lock $ready; $ready = 1; cond_signal($ready); }
      $thread_func->();
   };

   # Wait for the thread to finish initializing.    
   { lock $ready; while (!$ready) { cond_wait($ready); } }
   return $thread;
}

my $thread = create_thread(\&test_handler);

say sprintf "[%s %s] Sending KILL signal", time, threads->tid;
$thread->kill('KILL');

say sprintf "[%s %s] Doing stuff", time, threads->tid;
sleep(2);
say sprintf "[%s %s] Done doing stuff", time, threads->tid;

# Wait for threads to exit.
say sprintf "[%s %s] Waiting for threads to exit", time, threads->tid;
$_->join() for threads->list();
say sprintf "[%s %s] Exiting", time, threads->tid;

输出:

[1513212149 1] Starting thread
[1513212149 1] Doing stuff
[1513212149 0] Sending KILL signal
[1513212149 0] Doing stuff
[1513212151 0] Done doing stuff
[1513212151 0] Waiting for threads to exit
[1513212153 1] Forcibly exiting thread
[1513212153 0] Exiting

重要请注意,信号并未中断sleep。没有发送实际的 POSIX 信号[1](因为您只能将这些信号发送到进程),因此不能中断操作系统调用。相反,Perl 会主动检查是否在大多数 Perl 操作码之间发送了信号[2]。但这意味着像 sleepm// 和 XS 函数调用这样的长时间运行的操作码可能会无限期地延迟信号处理程序。


  1. 你很幸运,因为你没有使用 POSIX 系统。
  2. 前段时间,它曾经是在每个操作码之间,但检查的次数减少了。我认为 Perl 现在对每个语句检查一次。

【讨论】:

    【解决方案2】:

    这似乎是某种竞争条件,当有更多情况发生时它会起作用。

    use warnings;
    use strict;
    use feature 'say';
    
    use threads;
    use threads::shared;
    
    my $thread_handler_on :shared = 0;
    
    sub test_handler {
        say "\tIn thread.";
        $SIG{'KILL'} = sub {
            $thread_handler_on = 1;
            say "\tSet flag, exiting the thread in handler";
            threads->exit();
        };
        sleep 3;
        say "\tSet handler";
        sleep 1;
        say "\tHandler done napping";
    }
    
    my $new_thread = threads->create( 'test_handler' );
    
    sleep 1;
    
    say "Sending 'kill' to thread";
    $new_thread->kill('KILL')->detach();
    
    sleep 5;
    
    say "Did handler run: $thread_handler_on";
    

    问题中的代码是正确的out of threads docs(以及detach)。 这打印出来

    在线程中。 向线程发送“kill” 设置标志,退出处理程序中的线程 处理程序是否运行:1

    如果没有主线程中的sleeps,我也会得到报告的行为。

    请注意,这些“信号”不是通过内核发送的,Threads signalling

    该模块提供的线程信号能力实际上并不通过操作系统发送信号。它在 Perl 级别模拟信号,以便在适当的线程中调用信号处理程序。

    【讨论】:

    • 我以前遇到过这样的问题 - 这是因为“kill”实际上不是一个信号,它是一个延迟的安全信号 - 只有在适合处理时才会发生(所以不会中断某些操作)。使用处理程序中断睡眠通常是安全的。
    • @Sobrique 是的,这不是操作系统传递的信号,这就是我包含报价和链接的原因(那里的内容比我引用的要多)。问题是由于竞争条件,而不是信号本身,如 ikegami 的回答中详细所示。 (内核信号可能不存在这样的问题——这就是你的意思吗?——但这没有实际意义,我们无法对其进行测试,因为这些信号无法进入特定线程)。
    猜你喜欢
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-15
    相关资源
    最近更新 更多