Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Perl多线程程序偶尔崩溃_Multithreading_Perl - Fatal编程技术网

Multithreading Perl多线程程序偶尔崩溃

Multithreading Perl多线程程序偶尔崩溃,multithreading,perl,Multithreading,Perl,我用Perl编写了一个程序,它利用了多线程。我使用这个程序来理解多线程是如何在Perl中实现的 首先简要介绍一下该程序打算做什么:它将从文本文件中读取URL列表,一次读取一个URL列表。对于每个URL,它将调用一个子例程(将URL作为参数传递),并向其发送HTTP HEAD请求。一旦收到HTTP响应头,它将从响应中打印服务器头字段 对于每个URL,它启动一个新线程,该线程调用上述子例程 问题:主要问题是程序有时会间歇性崩溃。其他时间运行正常。这似乎是不可靠的代码,我相信有一种方法可以使它可靠地工

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