我有一个小的示例程序挂在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();
答案 0 :(得分:3)
问题与线程无关。 Perl操作之间的信号仅为processed,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!
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
安装信号处理程序。这使得一个特定信号“不安全”。