是否加入perl线程阻止SIGALRM?

时间:2016-06-07 00:49:58

标签: multithreading perl

我有一个小的示例程序挂在perl 5.16.3上。我试图使用alarm来触发如果两个线程在一个更复杂的程序中没有及时完成工作,但这归结为它的要点。我知道还有很多其他方法可以做到这一点,但是为了争论,让我们说我一直坚持使用代码。我不确定这是perl中的错误,还是合法不应该工作的错误。

我在互联网上对此进行了研究,似乎通常不鼓励混合警报和线程,但我已经看到很多例子,人们声称这是完全合理的事情,比如这个问题,Perl threads with alarm。在该问题上接受的答案中提供的代码也依赖于我的系统,这就是为什么我想知道这可能是现在已经破坏的,至少从5.16.3开始。

在下面的代码中,如果我在join关闭之前致电alarm,则alarm永远不会触发。如果我将join替换为while(1){}并进入忙等待循环,那么alarm就会很好,因此join似乎阻止了SIGALRM某种原因。

我的期望是join发生,然后几秒钟后我看到"警告!"打印在屏幕上,但只要在join关闭之前调用alarm,就不会发生这种情况。

#!/usr/bin/env perl

use strict;
use warnings;
use threads;

sub worker {
    print "Worker thread started.\n";
    while(1){}
}

my $thread = threads->create(\&worker);

print "Setting alarm.\n";
$SIG{ALRM} = sub { print "Alarm!\n" };
alarm 2;

print "Joining.\n";
$thread->join();

2 个答案:

答案 0 :(得分:3)

问题与线程无关。 Perl操作之间的信号仅为processedjoin的编号用C编写,因此只有在join返回时才会处理信号。以下内容证明了这一点:

#!/usr/bin/env perl

use strict;
use warnings;
use threads;

sub worker {
    print "Worker thread started.\n";
    for (1..5) {
       sleep(1);
       print(".\n");
    }
}

my $thread = threads->create(\&worker);

print "Setting alarm.\n";
$SIG{ALRM} = sub { print "Alarm!\n" };
alarm 2;

print "Joining.\n";
$thread->join();

输出:

Setting alarm.
Joining.
Worker thread started.
.
.
.
.
.
Alarm!

join基本上是对pthread_join的调用。与其他阻塞系统调用不同,pthread_join不会被信号中断。

顺便说一下,我将$tid重命名为$thread,因为threads->create返回一个线程对象,而不是一个线程ID。

答案 1 :(得分:1)

我将回答我自己的问题,为上面的ikegami回复添加一些细节,并总结我们的对话,这将使未来的访问者不必阅读它收集的巨大评论记录。

在与ikegami讨论之后,我去了更多关于perl信号的阅读,咨询了其他一些perl专家,并发现了join没有被解释器“中断”的确切原因。正如池上所说,信号只能在perl操作之间传递。在perl中,这称为Deferred Signals, or Safe Signals

延迟信号在2002年发布了5.8.0,这可能是我在网上发现看似不起作用的旧帖子的原因之一。他们可能使用“不安全信号”,这更像是我们在C中习惯的信号传递。事实上,从5.8.1开始,您可以通过设置环境变量PERL_SIGNALS=unsafe来关闭延迟信号传递在执行脚本之前。当我这样做时,threads::join调用确实会像我期望的那样被中断,就像在同一场景中C中断pthread_join一样。

与其他I / O操作(如read不同,它在信号中断时返回EINTR,threads::join不执行此操作。在引擎盖下,它是对C库调用pthread_join的调用,手册页确认不会返回EINTR。在延迟信号下,当解释器获得SIGALRM时,它会调度信号的传递,推迟信号,直到threads::join - > pthread_join库调用返回。由于pthread_join没有“中断”并返回EINTR,我的SIGALRM实际上被threads::join吞噬了。通过其他I / O操作,它们将“中断”并返回EINTR,使perl解释器有机会传递信号,然后通过SA_RESTART重新启动系统调用。

显然,在不安全信号模式下运行可能是一件坏事,因此根据perlipc,您可以使用POSIX模块直接通过sigaction安装信号处理程序。这使得一个特定信号“不安全”。

相关问题