从另一个中递归调用一个匿名子是否安全?

时间:2015-09-29 18:41:07

标签: perl mason

我想使用匿名子而不是命名子的原因是因为我想在Mason子组件(http://www.masonbook.com/book/chapter-2.mhtml#TOC-ANCHOR-7)中定义这些子组件,这些子组件在命名子组件中表现不佳。

E.g。如果我以这种方式编写代码:

my ($first, $second);
$first = sub {
    my $val = shift;
    print "val: $val";
    $second->($val);
};
$second = sub {
    my $val = shift;
    if (0 < $val) {
        $val = $val - 1;
        $first->($val);
    }
};
$first->(10);

这种方法中是否存在任何隐藏的陷阱(例如内存泄漏等)?

正如@Schwern所解释的那样,这些潜艇的记忆不会被Perl发布,因为它们之间存在循环引用。

但更具体地说,内存分配是否会线性增长,因为$ val会增加,或者它不依赖于调用堆栈的深度?因为我可以将这些潜艇放在梅森&lt;%once&gt;块,在这种情况下,这些子只会被初始化一次。

2 个答案:

答案 0 :(得分:5)

我唯一能想到的是,即使$first$second超出范围,子例程也永远不会被释放。 $first代码引用$second$second的代码引用$first。这是一个循环数据结构,Perl的内存分配无法解除分配。

$ perl -wlE 'for (1..10_000) { my($first, $second); $first = sub {};  $second = sub {} } say "Done"; sleep 1000'

$ perl -wlE 'for (1..10_000) { my($first, $second); $first = sub { $second->() }; $second = sub { $first->() } } say "Done"; sleep 1000'

第一个Perl进程在循环后使用1912K,第二个使用10320K。无论创建多少CV,第一个都不会增长,第二个将会。

要解决这个问题,您必须通过取消定义$first$second来打破这个圈子。第三个调用循环中的undef $first,其内存不会增长。

$ perl -wlE 'for (1..100_000) { my($first, $second); $first = sub { $second->() }; $second = sub { $first->() }; undef $first; } say "Done"; sleep 1000'

答案 1 :(得分:2)

以下情况可以接受:

sub first {
    my $val = shift;
    print "val: $val";
    second($val);
}

sub second {
    my $val = shift;
    if (0 < $val) {
        $val = $val - 1;
        first($val);
    }
}

first(10);

唯一需要注意的是,如果他们有原型,或者如果你想省略围绕他们的论点的parens,你需要声明潜艇。

sub first($);
sub second($);

sub first($) {
    my $val = shift;
    print "val: $val";
    second $val;
}

sub second($) {
    my $val = shift;
    if (0 < $val) {
        $val = $val - 1;
        first $val;
    }
}

first 10;

另一方面,您的版本存在内存泄漏。第一个子捕获对第二个子的引用,该子引用捕获对第一个子的引用。

$ perl -e'
   sub DESTROY { print "Destroyed\n" }

   {
      my ($first, $second);
      $first = sub { $second };
      $second = sub { $first };
      bless($first);
   }

   print("Subs should have been destroyed by now\n");
'
Subs should have been destroyed by now
Destroyed

解决方案取决于你为什么决定首先使用anon subs。