2015-09-29 2 views
5

Причина, по которой я хочу использовать анонимные субтитры вместо названных, заключается в том, что я хочу определить эти подмножества внутри подкомпонентов Мейсона (http://www.masonbook.com/book/chapter-2.mhtml#TOC-ANCHOR-7), которые не ведут себя хорошо с названными subs.Безопасно ли рекурсивно вызывать один анонимный субподрядчик из другого?

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 или оно не зависит от глубины стека вызовов? Потому что я могу поместить эти субмарины в mason <% once> blocks, и в этом случае эти subs будут инициализированы только один раз.

ответ

4

Единственный, о котором я могу думать, это то, что подпрограммы никогда не будут освобождены, даже если $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' 
+0

Спасибо, ребята! Но в основном, если я определяю анонимные субтитры вне цикла, мой объем памяти не будет повышаться? – Yakov

+1

Петля только преувеличивает проблему; вам все равно придется отменить $ first или $ second функцию, чтобы избежать круговой структуры. –

+0

@Yakov Петля только для иллюстрации утечки. Он A) предоставляет лексический контекст (т. Е. Блок), в котором подпрограммы в противном случае были бы уничтожены при выходе, а B) преувеличивает утечку памяти, поэтому он будет отображаться в грубом инструменте, таком как 'ps'. – Schwern

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

Единственный нюанс в том, что вам нужно, чтобы объявить подводные лодки, если у них есть прототип, или если вы хотите, чтобы опустить скобки вокруг своих аргументов.

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 

Решение зависит от того, почему вы решили использовать анонсы в первую очередь.

Смежные вопросы