Pregunta

Tengo un proyecto Perl eran sólo tenía un problema al hacer una llamada paquete circular. El código siguiente muestra el problema.

Cuando se ejecuta esto, cada paquete va a llamar a la otra hasta que toda la memoria del ordenador se consume y se bloquea. Estoy de acuerdo en que este es un mal diseño y que las llamadas circulares de este tipo no debe hacerse en el diseño, pero mi proyecto es lo suficientemente grande que quisiera detectar esto en tiempo de ejecución.

He leído acerca de la función de debilitar y de datos :: Estructura :: Util, pero no he encontrado una manera de detectar si hay una carga de paquete circular (soy suponer, debido a que una nueva copia se está haciendo en cada iteración y almacenada en cada copia del $ este hash). ¿Alguna idea?

use system::one;

my $one = new system::one(); 

package system::one;

use strict;

use system::two;

sub new {
  my ($class) = @_; 
  my $this = {};  
  bless($this,$class); 
  # attributes
  $this->{two} = new system::two();
  return $this; 
} 

package system::two;

use strict;

use system::one;

sub new {
  my ($class) = @_; 
  my $this = {};  
  bless($this,$class); 
  # attributes
  $this->{one} = new system::one();
  return $this; 
} 
¿Fue útil?

Solución

A continuación, tienen algo de código también. :)

sub break_recursion(;$) {
    my $allowed = @_ ? shift : 1;
    my @caller = caller(1);
    my $call = $caller[3];
    my $count = 1;
    for(my $ix = 2; @caller = caller($ix); $ix++) {
        croak "found $count levels of recursion into $call"
            if $caller[3] eq $call && ++$count > $allowed;
    }
}

sub check_recursion(;$) {
    my $allowed = @_ ? shift : 1;
    my @caller = caller(1);
    my $call = $caller[3];
    my $count = 1;
    for(my $ix = 2; @caller = caller($ix); $ix++) {
        return 1
            if $caller[3] eq $call && ++$count > $allowed;
    }
    return 0;
}

Estos se denominan como:

break_recursion(); # to die on any recursion
break_recursion(5); # to allow up to 5 levels of recursion
my $recursing = check_recursion(); # to check for any recursion
my $recursing = check_recursion(10); # to check to see if we have more than 10 levels of recursion.

Puede que estos CPAN, creo. Si alguien tiene alguna idea sobre esto, por favor compartir.

Otros consejos

El hecho de que estos se encuentran en paquetes separados no tiene nada que ver con el hecho de que este se ejecuta infinitamente, consumiendo todos los recursos disponibles. Estás llamando dos métodos de unas dentro de otras. Esto no es referencia circular, es recursión , que no es lo mismo. En particular, weaken no le ayudará en absoluto. Te obtener exactamente el mismo efecto de:

sub a {
    b();
}

sub b {
    a();
}

a();

La mejor manera de evitar esto es no haga eso . Más útil, si tiene que escribir funciones recursivas tratar de no utilizar varias funciones en la cadena de recursividad, sino simplemente el uno, por lo que tiene un tiempo más fácil mentalmente hacer el seguimiento de sus llamadas donde deben terminar.

En cuanto a la forma de detectar si algo como esto está ocurriendo, que tendría que hacer algo tan simple como una variable incrementa con el nivel de recursividad y terminar (o retorno) si su profundidad excede de un cierto valor. Pero que realmente no debería tener que depender de eso, es similar a escribir un bucle while y utilizando un incremento allí para asegurarse de que su función no se ejecuta fuera de control. Eso sí, no recursivo sobre un conjunto a menos que sepa cómo y cuándo se termina.

Otra cuestión relevante sería lo que estás tratando de lograr en primer lugar?

Sugiero hacer una rutina llamada algo así como break_constructor_recursion () que utiliza la persona que llama () para examinar la pila de llamadas, así:

Para saber qué método en qué paquete me acaba de llamar.

Consulta el resto de la pila de llamadas de ver si ese mismo método que en el mismo paquete está en cualquier lugar más arriba.

Si es así, morir () con algo apropiado.

A continuación, se agrega una llamada a break_constructor_recursion () en sus constructores. Si el constructor se está llamando desde el interior de sí mismo, que va a bombardear a cabo.

Ahora, esto puede arrojar falsos positivos; no es imposible que un constructor que se llama legítimamente dentro de sí mismo. Si usted tiene problemas con eso, diría que acaba de buscar algún n apariciones del constructor antes de que se identifica un error. Si hay 20 llamadas a sistema de dos :: :: nuevo () en la pila, las posibilidades de que usted no está de manera recursiva son bastante bajos.

La ruptura clásica en doble recursividad es utilizar una variable de estado para determinar si ya se encuentra dentro de una función:

{
    my $in_a;
    sub a {
        return if $in_a; #do nothing if b(), or someone b() calls, calls a()
        $in_a = 1;
        b();
        $in_a = 0;
    }
}

Puede hacer lo que quiera si $in_a es cierto, pero dieing o devolver es común. Si está utilizando Perl 5.10 o más tarde se puede utilizar la función state lugar de anidación de la función en su propio ámbito de aplicación:

sub a {
    state $in_a;
    return if $in_a; #do nothing if b(), or someone b() calls, calls a()
    $in_a = 1;
    b();
    $in_a = 0;
}

use warnings;

sin advertencias:

#!/usr/bin/perl 

use strict;

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl 
^C # after death 

con advertencias:

#!/usr/bin/perl 

use strict;
use warnings;

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl 
Deep recursion on subroutine "main::foo" at script.pl line 7.
^C # after death 

Siempre utilice siempre las advertencias.

use warnings FATAL => qw( recursion );

#!/usr/bin/perl 

use strict;
use warnings FATAL => qw( recursion );

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl 
Deep recursion on subroutine "main::foo" at script.pl line 7.
$ 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top