动态变量和承诺

use*_*601 6 multithreading perl6 raku

似乎动态变量并不总是能在线程中的子例程调用中幸存:

sub foo($x, &y = &infix:<+>) {
  my &*z = &y;
  bar($x);
}

sub bar ($x) {
    say &*z($x,$x);

    my $promise = start { bar($x-1) if $x > 0 }
    await $promise;

    # bar($x-1) if $x > 0    # <-- provides the expected result: 6, 4, 2, 0
}
foo(3); # 6, 4, Dynamic variable &*z not found
Run Code Online (Sandbox Code Playgroud)

使用全局范围更广的变量也可以,因此并不是所有变量都丢失了–似乎是动态变量:

our &b;

sub foo($a, &c = &infix:<+>) {
  &b = &c;
  bar($a);
}

sub bar ($a) {
    say &b($a,$a);
    my $promise = start { bar($a-1) if $a > 0 }
    await $promise;
}

foo(3); # 6, 4, 2, 0
Run Code Online (Sandbox Code Playgroud)

一旦将变量设置为foo(),就可以毫无问题地读取它bar()。但是,当bar()从promise内部调用时,for的值&*z不会在递归的第一层消失,而在第二层消失。

我感觉到一个错误,但也许我在递归/动态变量/线程之间做些奇怪的事情,使事情变得混乱。

Jon*_*ton 8

在当前语义下,start将捕获在其内调用的上下文。如果动态变量查找在start执行该线程的堆栈(线程池中的线程池之一)上失败,则将退回到查看所捕获的动态范围当start块调度。

start在执行另一个start块的过程中创建一个块时,也会发生同样的事情。但是,两者之间没有关系,这意味着start也不会搜索“外部” 块捕获的上下文。尽管可以为这种情况辩护,但这样做似乎有潜在的问题。考虑以下示例:

sub tick($n = 1 --> Nil) {
    start {
        await Promise.in(1);
        say $n;
        tick($n + 1);
    }
}
tick();
sleep;
Run Code Online (Sandbox Code Playgroud)

这是每秒产生刻度的一种(并非完全惯用的)方法。如果内部start为了保持动态变量查找的目的而保留了对外部状态的引用,则此程序将在内存中建立一个链,链的长度不断增加,这似乎是不受欢迎的行为。