perl - setalarm() 显然没有调用我的处理程序
问题描述
我已经使用 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
解决方案
根据线程文档:
信号被脚本的主线程(线程 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
推荐阅读
- python - 如何删除第一次出现的字母?
- javascript - 折叠和展开 div 而不出现滚动条
- javascript - 在 React 和 JSX 中使用状态值对数组进行排序
- webrtc - WebRtc,onaddstream,接收,流,不显示?
- node.js - 在生产服务中构建 Vue.js
- python - python3运行旧版脚本
- azure-active-directory - Microsoft Graph API 调用 /me 端点失败并出现错误:compactToken 80049217
- python - 如何使用这个“|”转换数据 pandas 中用于推荐系统的符号
- android - 异步执行任务时阻塞Android上的主线程
- javascript - 通过提交将文本输入到超链接