所以我最近想通过一个Perl程序来提高速度。收集网站列表,我想为每个网址启动一个帖子并获取每个网站的内容,然后在页面上查找公司描述。一旦一个线程找到了结果,或者所有线程都没有,我想退出,写下我的结果,并在我的下一个公司的URL中读取。
我看到的问题是我在创建线程时使用的函数内部使用了Perl :: Unsafe :: Signals模块。我需要不安全的信号来中断被“卡住”的正则表达式。然而,这似乎会导致各种问题,主要是程序崩溃并显示错误信息“闹钟”。
因此,有没有办法安全地使用Perl :: Unsafe :: Signals和线程?有没有办法通过向函数发送信号以另一种方式超时正则表达式(就像我在下面发送'KILL'信号?)谢谢。
注意:我将代码删除到所有相关部分,如果您需要更多,请告诉我。
use threads ('exit' => 'threads_only');
use threads::shared;
my @descrip;
share(@descrip);
my $lock;
share($lock);
URL:foreach my $url(@unique_urls) {
#skip blank urls
if(!$url) { next URL; }#if
#find description
my $thread = threads->create(\&findCompanyDescription, $PREV_COMPANY, $PREV_BASE_URL, $url);
#while a description has not been found and there are still active threads, keep looking
#there may be a better way to do this, but this seems to work for me
while(!@descrip && threads->list() != 0) {;}
#kill all threads, write output, read in next batch of urls
my @threads = threads->list();
foreach(@threads) { print("detaching\n"); $_->kill('KILL')->detach(); }#foreach
#######由线程创建的SUBRUTINE
sub findCompanyDescription {
my($company_full, $base_url, $url) = @_;
my($descrip, $raw_meta, $raw) = '';
my @company;
$SIG{'KILL'} = sub { alarm(0); threads->exit(); };
eval {
local $SIG{ALRM} = sub { die("alarm\n") }; # NB: \n required
alarm(5);
use Perl::Unsafe::Signals;
UNSAFE_SIGNALS {
while($company) {
my @matches = ($content =~ m!.*<([\w\d]+).*?>\s*about\s+$company[\w\s\-_]*<.*?>(?:<.*?>|\s)*(.*?)</\1.*?>!sig);
MATCH:for(my $ndx=1; $ndx<@matches; $ndx+=2) {
($raw, $descrip) = &filterResult($matches[$ndx], $company_full);
if($descrip) {
$company = undef;
last(MATCH);
}#if
}#for
#reduce the company name and try again
$company = &reduceCompanyName($company);
}#while
alarm(0);
};#unsafe_signals
};#eval
if($@) {
if($@ eq "alarm\n" && $DEBUG) { print("\nWebpage Timeout [].\n"); }#if
}#if
if($descrip) { lock($lock); {
@descrip = ($PREV_ID, $company_full, $base_url, $url, 1, $raw, $descrip); }
}#if
答案 0 :(得分:7)
通常,“不安全”信号对单线程和多线程都不安全。你只是通过使用线程和不安全信号来增加你的危险。 Perl通常的安全信号处理程序设置标志signal_pending
,而不会有意义地中断执行。 VM在操作码之间检查该标志。
你的正则表达式执行是一个单一的“原子”操作码。当然,regexp本身是另一个拥有自己的操作码的虚拟机,但我们目前没有对perl信号处理程序的可见性。
坦率地说,我对如何中断正则表达式引擎并不了解。它有一些全局C状态,在过去之前perl-5.10阻止它可以重入。像你一样尝试通用中断可能不安全。如果你真的希望它完全可以中断,你可能需要fork并让你的子进程执行regexp并通过管道传回结果。
require JSON;
require IO::Select;
my $TIMEOUT_SECONDS = 2.5; # seconds
my ( $read, $write );
pipe $read, $write;
my @matches;
my $pid = fork;
if ( $pid ) {
my $select = IO::Select->new( $read );
if ( $select->can_read( $TIMEOUT_SECONDS ) ) {
local $/;
my $json = <$read>;
if ( $json ) {
my $matches_ref = JSON::from_json( $json );
if ( $matches_ref ) {
@matches = @$matches_ref;
}
}
}
waitpid $pid, 0;
}
else {
my @r = $content =~ m!.*<([\w\d]+).*?>\s*about\s+$company[\w\s\-_]*<.*?>(?:<.*?>|\s)*(.*?)</\1.*?>!sig;
my $json = JSON::to_json( \ @r );
print { $write } $json;
close $write;
exit;
}
答案 1 :(得分:2)
如果我没记错,SIGALRM是异步信号,因此必须同步处理。在多线程程序中,处理它的方式通常是不正确的。
此外,恕我直言perl线程只是不像大多数人所期望的那样工作。 只是避免使用它们而是使用流程。
P.S。
以下行没有意义:
$SIG{'KILL'} = sub { alarm(0); threads->exit(); };
SIGKILL(以及SIGSTOP)无法被捕获。
答案 2 :(得分:1)
我不是Perl-MT的专家,但你显然缺少的一件事是信号在整个过程中是全局的 - 它们不是特定于线程的。在POSIX系统上,您无法为线程设置信号处理程序:信号将传递到整个过程。 IOW alarm()
调用会影响整个过程,而不仅仅是调用它的线程。甚至MT上下文中的local %SIG
也没有做到人们可能认为的那样 - 因为local
是一种语法。