加入perl线程会阻塞SIGALRM吗



我有一个挂在perl 5.16.3上的小示例程序。如果两个线程在一个复杂得多的程序中没有及时完成工作,我试图使用alarm来触发,但这可以归结为要点。我知道有很多其他方法可以做到这一点,但为了便于讨论,假设我一直坚持代码的现状。我不确定这是perl中的一个错误,还是一些不应该正常工作的东西。

我在互联网上研究过这一点,似乎通常不鼓励将警报和线程混合在一起,但我看到了很多例子,人们声称这是一件非常合理的事情,比如另一个SO问题,带警报的Perl线程。关于这个问题的公认答案中提供的代码也挂在我的系统上,这就是为什么我想知道这是否是现在坏了的东西,至少在5.16.3之前。

在下面的代码中,如果我在alarm关闭之前调用join,则alarm永远不会触发。如果我用while(1){}替换join并进入繁忙等待循环,那么alarm会正常工作,因此join似乎出于某种原因阻塞了SIGALRM。

我的期望是join发生,几秒钟后我看到屏幕上打印着"警报!",但这永远不会发生,只要joinalarm熄灭之前被调用。

#!/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();

问题与线程无关。信号只在Perl操作之间进行处理,而join是用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!

CCD_ 14实质上是对CCD_。与其他阻塞系统调用不同,pthread_join不会被信号中断。


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

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

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

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

与其他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安装信号处理程序。这就使得一个特定的信号"不安全"。

最新更新