Почему этот Perl-скрипт постепенно исчерпывает память

У меня проблема с запуском Perl-скрипта в многопоточном режиме. Он продолжал потреблять память, и, наконец, системе не хватило памяти, и он убил ее. Кажется, что подпотоки были отключены, но системный ресурс не был освобожден, когда они закончились. Я довольно новичок в Perl и не мог найти, какая часть пошла не так. Это часть сценария, которая может вызвать эту проблему. Может ли кто-нибудь помочь мне с этим?

use strict;
use warnings;

print "different number:\t";
my $num1=<>;
chomp $num1;
if($num1!~/[1 2 3 4 5]/)
 {
    print "invalid input number\n";
    END;
 }

my $i=0;
my $no;
my @spacer1;
my $nn;
my @spacer2;

open IN,"file1.txt"or die"$!";
  while(<IN>)
   {
     chomp;
     if($_=~ /^>((\d)+)\|((\d)+)/)
       {         
         $no=$1;
         $spacer1[$no][0]=$3;        
       }
     else
       {
         $spacer1[$no][1]=$_;       
       }
   }
close IN;

open IN, "file2.txt" or die "$!";
  while(<IN>)
   {
     chomp;
     if($_=~ /^>((\d)+)\|((\d)+)/)
       {         
         $nn=$1;
         $spacer2[$nn][0]=$3;       
       }
     else
       {
         $spacer2[$nn][1]=$_;       
       }
   }
close IN;

#-----------------------------------------------------------------#create threads
use subs qw(sg_ana);
use threads;
use Thread::Semaphore;


my $cycl=(int($no/10000))+1;
my $c;
my @thd;
my $thread_limit= Thread::Semaphore -> new (3);

foreach $c(1..$cycl)
  {
    $thread_limit->down();
    $thd[$c]=threads->create("sg_ana",$c-1,$c,$num1);
    $thd[$c]->detach();
  }
&waitquit;

#-------------------------------------------------------------#limite threads num
sub waitquit 
  {
    print "waiting\n";
    my $num=0;
    while($num<3)
      {
        $thread_limit->down();
        $num++;
      }         
  }

#---------------------------------------------------------------#alignment
my $n;
my $n1;
my $j;
my $k;
my $l;
my $m;
my $num;#number of match
my $num2=0;;#arrange num



sub sg_ana
  {
    my $c1=shift;
    my $c2=shift;
    $num1=shift;
    open OUT,">$num1.$c2.txt" or die "$!";   
    if($num1==1)
      {
        foreach $n($c1*10000..$c2*10000-1)
          {
            if($spacer2[$n][1])
              {
                my $presult1;
                my $presult2;
                $num2=-1;
                foreach $i(0..19)
                  {
                    $num=0;
                    $num2++;
                    my $tmp1=(substr $spacer2[$n][1],0,$i)."\\"."w".(substr $spacer2[$n][1],$i+1,19-$i);
                    foreach $n1(0..@spacer1-1)
                      {
                        if($spacer1[$n1][1])
                          {
                            my $tmp2=substr $spacer1[$n1][1],0,20;
                            if($tmp2=~/$tmp1/)
                              {
                                $num++; 
                                $presult1.=$n1.",";                
                              } 
                          }
                      }        
                    $presult2=$i+1; 
                    if($num>=4)
                      { 
                        print OUT "\n";
                      }
                  }
              }
          }
      }
    close OUT;
    $thread_limit->up();
  }

person c11cc    schedule 24.11.2016    source источник
comment
Это весь ваш код? Ваш $num1 никогда не определяется, что делает подпрограмму sg_ana почти неработающей. (Это то, о чем use warnings предупредил бы вас)   -  person oals    schedule 24.11.2016
comment
Это только часть кода. До этой части он читал из файлов. И я уверен, что в этой части нет проблем.   -  person c11cc    schedule 25.11.2016
comment
Ваш отредактированный код все еще содержит синтаксическую ошибку. Здесь будет полезен минимальный воспроизводимый пример.   -  person Sobrique    schedule 25.11.2016
comment
Спасибо за ваше предложение, я проверил его. Теперь нет синтаксической ошибки.   -  person c11cc    schedule 25.11.2016
comment
use subs? Это что, 1998 год?   -  person melpomene    schedule 25.11.2016
comment
Кажется, что это не имеет смысла. Это ошибка. @melpomene   -  person c11cc    schedule 25.11.2016


Ответы (1)


  1. Первое правило отладки Perl — включить use strict; и use warnings;, а затем разобраться с ошибками. На самом деле вам, вероятно, следует сделать это в первую очередь, прежде чем вы даже начнете писать код.
  2. Вы создаете и ограничиваете потоки с помощью семафора, но на самом деле это действительно неэффективно из-за того, как perl делает потоки - они не легкие, поэтому создание нагрузок - плохая идея. Лучший способ сделать это — через Thread::Queue что-то вроде этого.
  3. Пожалуйста, используйте дескрипторы открытых и лексических файлов с 3 аргументами. например open ( my $out, '>', "$num.$c2.txt" ) or die $!;. Вам, вероятно, здесь это сойдет с рук, но вы получили OUT в качестве глобальной переменной пространства имен, используемой несколькими потоками. На этом пути лежат драконы.
  4. Не используйте однобуквенные переменные. А учитывая, как вы используете $c, вам будет намного лучше:

    foreach my $value ( 1..$cycl ) { 
        ##  do stuff
    }
    

То же самое относится и ко всем вашим другим однобуквенным переменным — они не имеют смысла.

  1. Вы передаете $num до его инициализации, поэтому в вашем подразделе всегда будет undef. Итак, ваша фактическая подпрограмма просто:

    sub sg_ana
      {
        my $c1=shift;
        my $c2=shift;
        $num1=shift;
        open OUT,">$num1.$c2.txt" or die "$!";   
    
        close OUT;
        $semaphore->up();
     }
    

Глядя на это, я думаю, что вы возможно пытаетесь что-то сделать с общей переменной, но на самом деле вы не делитесь ею. Однако я не могу расшифровать логику вашей программы (скорее всего, из-за того, что у меня много однобуквенных переменных), поэтому я не могу сказать наверняка.

  1. Вы вызываете подпрограмму &waitquit;. Это нехороший стиль — добавление префикса с амперсандом и отсутствие аргументов делает что-то немного отличающееся от простого вызова подпрограммы «обычно», поэтому вам следует избегать этого.
  2. Не создавайте экземпляр своего семафора следующим образом:

    my $semaphore=new Thread::Semaphore(3);
    

Это косвенный вызов процедуры и плохой стиль. Лучше было бы написать так:

my $thread_limit = Thread::Semaphore -> new ( 3 ); 
  1. Я бы посоветовал вместо того, чтобы использовать такие семафоры, вам было бы гораздо лучше не detatch создавать свои потоки, а просто использовать join. Вам также не нужен массив потоков — threads -> list сделает это за вас.

  2. Я не могу воспроизвести вашу проблему, потому что ваш саб ничего не делает. Вы случайно не модифицировали его для публикации? Но классическая причина нехватки памяти Perl при многопоточности заключается в том, что каждый поток клонирует родительский процесс, поэтому 100 потоков — это в 100 раз больше памяти.

person Sobrique    schedule 24.11.2016
comment
Большое спасибо. Это действительно помогает. - person c11cc; 25.11.2016
comment
1. Перед этой частью используются строгие и предупреждения, потому что я уверен, что эта часть в порядке, поэтому просто не стал их публиковать. - person c11cc; 25.11.2016
comment
2 Я заметил, что OUT используется среди потоков, но я не знаю, как с этим бороться. Я постараюсь изменить это. - person c11cc; 25.11.2016
comment
3. В sub sg_ana ошибка $num, на самом деле я изменил эту часть перед публикацией. - person c11cc; 25.11.2016
comment
4. Я не знаю, как ограничить количество запущенных потоков, и я считаю, что семофоры работают, поэтому я просто использую их. Я понятия не имею о том, что лучше. Я просто хочу отделить потоки и упорядочить число, которое выполняется одновременно. - person c11cc; 25.11.2016
comment
Я не знаю, почему он использует память. Я отсоединил темы, но это не сработало? - person c11cc; 25.11.2016
comment
Если в вашем примере кода нет проблемы, то мне почти невозможно понять, что это было. - person Sobrique; 25.11.2016
comment
Я разместил весь код. Он работал нормально, но потреблял память постепенно. Может быть, я могу попробовать join, а не detach. - person c11cc; 25.11.2016
comment
Учитывая, что вы явно ждете завершения ваших потоков, прежде чем создавать новые, тогда join — это именно тот механизм, который вы хотите использовать. detatch предназначен для «если вам просто все равно», и если вы находитесь в этом сценарии ... вы, вероятно, не хотите использовать потоки в первую очередь. - person Sobrique; 25.11.2016
comment
Да, вы правы, join именно тот, который мне нужен. Просто было сказано, что detatch проще в использовании, поэтому он сначала попробовал его. - person c11cc; 25.11.2016
comment
Но классическая причина нехватки памяти perl при многопоточности заключается в том, что каждый поток клонирует родительский процесс, поэтому 100 потоков — это 100-кратный объем памяти. Значит ли это, что даже если я использую join, исчерпание памяти также бывает? - person c11cc; 26.11.2016
comment
joining поток должен восстановить память. Но если вы создаете по одному экземпляру, то fork — оптимальный инструмент. threads лучше подходят для работы с моделью рабочих потоков, когда вы создаете фиксированное количество потоков для выполнения N-путевого параллелизма. - person Sobrique; 29.11.2016
comment
Я последовал вашему совету и последовал этому. Он работал в течение одного дня, и потребление памяти не сильно увеличилось. Модель рабочего потока — действительно отличная идея. - person c11cc; 29.11.2016