Multithreading Perl多线程程序偶尔崩溃
我用Perl编写了一个程序,它利用了多线程。我使用这个程序来理解多线程是如何在Perl中实现的 首先简要介绍一下该程序打算做什么:它将从文本文件中读取URL列表,一次读取一个URL列表。对于每个URL,它将调用一个子例程(将URL作为参数传递),并向其发送HTTP HEAD请求。一旦收到HTTP响应头,它将从响应中打印服务器头字段 对于每个URL,它启动一个新线程,该线程调用上述子例程 问题:主要问题是程序有时会间歇性崩溃。其他时间运行正常。这似乎是不可靠的代码,我相信有一种方法可以使它可靠地工作 守则:Multithreading Perl多线程程序偶尔崩溃,multithreading,perl,Multithreading,Perl,我用Perl编写了一个程序,它利用了多线程。我使用这个程序来理解多线程是如何在Perl中实现的 首先简要介绍一下该程序打算做什么:它将从文本文件中读取URL列表,一次读取一个URL列表。对于每个URL,它将调用一个子例程(将URL作为参数传递),并向其发送HTTP HEAD请求。一旦收到HTTP响应头,它将从响应中打印服务器头字段 对于每个URL,它启动一个新线程,该线程调用上述子例程 问题:主要问题是程序有时会间歇性崩溃。其他时间运行正常。这似乎是不可靠的代码,我相信有一种方法可以使它可靠地工
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use WWW::Mechanize;
no warnings 'uninitialized';
open(INPUT,'<','urls.txt') || die("Couldn't open the file in read mode\n");
print "Starting main program\n";
my @threads;
while(my $url = <INPUT>)
{
chomp $url;
my $t = threads->new(\&sub1, $url);
push(@threads,$t);
}
foreach (@threads) {
$_->join;
}
print "End of main program\n";
sub sub1 {
my $site = shift;
sleep 1;
my $mech = WWW::Mechanize->new();
$mech->agent_alias('Windows IE 6');
# trap any error which occurs while sending an HTTP HEAD request to the site
eval{$mech->head($site);};
if($@)
{
print "Error connecting to: ".$site."\n";
}
my $response = $mech->response();
print $site." => ".$response->header('Server'),"\n";
}
上述异常代码对应于:状态\无效\句柄
这可能对应于线程的无效句柄
我的Perl安装的详细信息:
Summary of my perl5 (revision 5 version 14 subversion 2) configuration:
Platform:
osname=MSWin32, osvers=5.2, archname=MSWin32-x86-multi-thread
useithreads=define
操作系统详细信息:Win7终极版64位操作系统
希望这些信息足以找到问题的根本原因并更正代码。我在perl中广泛使用多线程来构建大型系统。 在我看来,启动线程并等待它们完成的部分很好 回答您的问题:
- 不需要睡觉
- 您调用join的方式是正确的,它基本上会阻塞,直到所有线程完成
- 尝试注释掉mechanize代码。只是为了确保这不是造成这一切的原因。可以在函数内部进行随机睡眠。看看你的脚本是否仍然崩溃
- 尝试删除多线程,并查看多次调用函数(使用for循环或其他方法)是否会导致任何问题
- 我突然想到的一点“最佳实践”是,您使用的是三参数open(良好)但不是一个裸字文件句柄(boo!)。我总是倾向于用“and”和“and”或“or”来代替“&&”和“or”。它们是优先级最低的运算符,因此(至少对我来说)也是最容易用来正确拆分命令的运算符。我倾向于只在三元运算符内部或在equals的右侧使用&&和| | |,就像我的$a=func()| |默认值一样
所以我要写一行:
open my $input, '<', 'urls.txt; or die "Couldn't open `urls.txt' for read: $!";
打开我的$input,”我建议改用可重用线程方法。请参见此示例:
还要检查优秀的线程::队列模块:
use threads;
use Thread::Queue;
my $q = Thread::Queue->new();
my $pq = Thread::Queue->new();
my $config = { number_of_threads => 10 };
my @threads = map { threads->create( \&worker, $q, $pq ) }
( 1 .. $config->{number_of_threads} );
push @threads, threads->create( \&controller, $q, $pq );
my @urls = read_urls($filename);
foreach my $url (@urls) {
process_url( $q, $url );
}
while ( my $pend = $q->pending() ) {
sleep 1;
}
$q->enqueue(undef) for @threads;
while ( my $pend = $pq->pending() ) {
sleep 1;
}
$pq->enqueue(undef);
foreach my $thr (@threads) {
$thr->join();
}
sub worker {
my ( $q, $pq ) = @_;
while ( my $url = $q->dequeue() ) {
my $result = check_url($url);
$pq->enqueue($result);
}
printf "Finishing tid(%s)\n", threads->tid;
return;
}
sub controller {
my ( $q, $pq ) = @_;
while ( my $result = $pq->dequeue() ) {
save_result($result);
}
printf "Finishing Controller tid(%s)\n", threads->tid;
return;
}
sub process_url {
my ( $q, $url ) = @_;
$q->enqueue($url);
return;
}
你的代码没有问题。可能是你的期望太高了一点
Perl的线程是通过在同一操作系统进程中创建几个解释器实例来实现的。这将每个线程中的Perl代码与所有其他线程隔离开来(不共享任何内容)。它没有(也不能)做的是隔离不受perl控制的代码。也就是说,任何带有用C编写的组件的模块。例如,快速查看一下WWW::Mechanize就可以看出,如果安装了zlib,它可以使用zlib进行压缩。如果使用了这种方法,并且C代码没有足够的线程安全性,那么可能会出现崩溃问题。因此,如果您想确保Perl应用程序在线程下运行良好,您必须检查它使用的所有模块(以及它们使用的所有模块),并检查它们是否没有非Perl部分,或者这些部分是线程安全的。对于大多数非平凡的程序来说,这是一个不合理的工作量(或者对可以使用的CPAN模块的不合理限制)
这可能是Perl中线程没有被大量使用的一个主要原因。+1为了在请求帮助之前彻底调查URL文件有多大?使用$mech->success
不是更好,而不是将调用包装在eval
中并检查$@
,如果要错开每个线程的开头,则睡眠
需要进入while
循环,而不是sub1
URL文件有多大?每个Perl线程的内存占用与调用进程的内存占用相同。启动太多线程会使程序崩溃。我认为很可能是模块出现了故障。我建议你把答案放在最上面,因为它现在隐藏在中间。
use threads;
use Thread::Queue;
my $q = Thread::Queue->new();
my $pq = Thread::Queue->new();
my $config = { number_of_threads => 10 };
my @threads = map { threads->create( \&worker, $q, $pq ) }
( 1 .. $config->{number_of_threads} );
push @threads, threads->create( \&controller, $q, $pq );
my @urls = read_urls($filename);
foreach my $url (@urls) {
process_url( $q, $url );
}
while ( my $pend = $q->pending() ) {
sleep 1;
}
$q->enqueue(undef) for @threads;
while ( my $pend = $pq->pending() ) {
sleep 1;
}
$pq->enqueue(undef);
foreach my $thr (@threads) {
$thr->join();
}
sub worker {
my ( $q, $pq ) = @_;
while ( my $url = $q->dequeue() ) {
my $result = check_url($url);
$pq->enqueue($result);
}
printf "Finishing tid(%s)\n", threads->tid;
return;
}
sub controller {
my ( $q, $pq ) = @_;
while ( my $result = $pq->dequeue() ) {
save_result($result);
}
printf "Finishing Controller tid(%s)\n", threads->tid;
return;
}
sub process_url {
my ( $q, $url ) = @_;
$q->enqueue($url);
return;
}