Perl Multi Threaded Program偶尔会崩溃

时间:2014-03-02 17:07:58

标签: multithreading perl

我在Perl中编写了一个使用多线程的程序。我正在使用这个程序来理解Perl中如何实现多线程。

首先简要概述程序打算做什么:它将从文本文件中一次读取一个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";
}

问题:

如何让这个程序可靠地运行?偶然崩溃的原因是什么?

调用线程对象的join方法的目的是什么?

根据以下链接的文档,它将等待线程执行完成。我是否正确调用了连接方法?

http://perldoc.perl.org/threads.html

如果我必须在上述代码中包含任何良好的编程习惯,请告知我们。

我是否需要在代码中专门调用sleep(),还是不需要?

在C中,我们在调用CreateThread()之后调用Sleep()来开始执行线程。

关于崩溃:当上面的Perl代码意外崩溃并偶尔崩溃时,我收到错误消息:“Perl命令行解释器已停止工作”

崩溃的详情:

Fault Module Name:  ntdll.dll
Exception Code: c0000008

上述异常代码对应于:STATUS_INVALID_HANDLE

也许这对应于线程的无效句柄。

我的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

操作系统的详细信息:Win 7 Ultimate,64位操作系统。

希望此信息足以找到问题的根本原因并更正代码。

4 个答案:

答案 0 :(得分:4)

您的代码没有任何问题。可能你的期望有点太高了。

通过在同一操作系统进程中创建多个解释器实例来实现Perl的线程。这将每个线程中的Perl代码与所有其他代码隔离开来(它没有共享)。它没有(并且不能)做的是隔离不受perl控制的代码。也就是说,任何具有用C语言编写的组件的模块。例如,快速浏览WWW :: Mechanize表明它能够使用zlib进行压缩(如果已安装)。如果使用它,并且C代码没有足够的线程安全性,那么这可能是一个可能崩溃的问题。因此,如果您想确保您的Perl应用程序在线程下运行良好,您必须浏览它使用的所有模块(以及他们使用的所有模块)并检查它们是否没有非Perl部件或那些部件是线程安全的。对于大多数重要的程序来说,这是一项不合理的工作量(或者对可以使用的CPAN模块的不合理限制)。

这可能是Perl中线程没有被广泛使用的原因之一。

答案 1 :(得分:2)

我在perl中广泛使用多线程来构建大型系统。 您启动线程并等待它们完成的部分对我来说很好。

回答你的问题:

  • 不需要睡眠。

  • 你调用join的方式是正确的,它基本上会阻塞直到所有线程完成。

我会做以下事情:

  • 尝试注释掉机械化代码。只是为了确保它不是造成这种情况的那个。可以在函数内部进行随机睡眠。看看你的脚本是否仍然崩溃。

  • 尝试删除多线程并查看是否多次调用该函数(具有for循环或其他内容)会导致任何问题。

答案 2 :(得分:0)

跳出来的一个小“最佳实践”的东西是你使用三个参数打开(好)但是一个裸字文件句柄(嘘!)。我总是倾向于使用“和”和“或”而不是“&amp;&amp;”和“或”也是。它们是最低优先级的运算符,因此(对我来说,至少)最容易用来正确分割命令。我倾向于使用&amp;&amp;和||只在三元运算符内或等于右边,就像我的$ a = func()|| '默认';

所以我写的那条开放线:

open my $input, '<', 'urls.txt; or die "Couldn't open `urls.txt' for read: $!";

答案 3 :(得分:0)

我建议使用可重用的线程方法。请参阅此示例:Reusable threads demo

还要检查优秀的Thread :: Queue模块:

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;
}