如何终止在BEGIN块中开始的循环?

9
我想在应用程序加载/初始化时显示进度条。
以下代码无法正常工作,但应该能让您了解我试图实现的目标。
my Bool $done-compiling = False;
BEGIN {
    start repeat {
        print '*';
        sleep 0.33;
    } until $done-compiling;
};

INIT {
    $done-compiling = True;
};

有没有可以在BEGIN块中响应的触发事件?
3个回答

7

Liz提供了一个解决方案,关于你编写的代码,修复了原先存在的错误。

以下是更简单的版本:

BEGIN start repeat { print '*'; sleep 0.33 } until INIT True

(你可能认为 INIT True 是某种特殊功能,但它实际上是由返回值的 phaser 的自然结果而产生的。)
(您可能认为 INIT True 是某种特殊功能,但它实际上是由返回值的 phaser 的自然结果而产生的。)

6

我认为这段代码没有问题。如果你模拟加载,你会看到一个由星号 * 组成的进度条:

my Bool $done-compiling;
BEGIN {
    start repeat {
        print '*';
        sleep 0.33;
    } until $done-compiling;
}

BEGIN sleep 3; # simulate compilation / loading

INIT $done-compiling = True;

INIT块中设置标志应该就足够了!我想你可以将INIT块的执行视为你正在寻找的事件吗?

1
请注意,默认情况下输出是缓冲的,因此打印 * 可能不会及时出现;您可能希望将此作为 BEGIN 块的第一行添加:BEGIN { $*OUT.out-buffer = False;如果需要,在完成 BEGIN/INIT 时间工作后,可以重新启用缓冲。 - Coke
1
使用欢迎来到Rakudo™ v2022.07。 实现Raku®编程语言v6.d。 基于MoarVM版本2022.07构建。 在macOS Monterey 12.5上。当我将这些行添加到您的程序中时:prompt "?? "; say "Done.";我得到以下输出:(我在提示符处键入了 hello world):**********?? *****h*e*ll*o*****wo*r*ld******** Done.进度条在程序开始执行后继续进行。 - Jim Bollinger
@Coke:如果 $*OUT.t,我很确定它是未缓冲的。至少对我来说是这样。 - Elizabeth Mattijsen
@JimBollinger 哦,是的,我的错。 INIT 会将标志设置为 True,然后正常执行开始。这在第1行发生,标志被重置为 False。删除 = False(就像我在示例中刚刚做的那样),它应该按照您的意图工作。 :-) - Elizabeth Mattijsen

2

您已经得到了两个可行的答案。但是它们都依赖于在不同线程之间使用变量,这总是让我有点紧张。鉴于我们需要多个线程,在这里我可能会转向Raku的一个有用的并发原语。下面是对接受答案进行微小更改的代码,使用了Promise

my Promise $done-compiling;
BEGIN {
    $done-compiling .= new;
    start repeat {
        print '*';
        sleep 0.33;
    } until $done-compiling ~~ Kept;
}

BEGIN sleep 3; # simulate compilation / loading

INIT $done-compiling.keep;

1
谢谢 - 这不仅优雅,而且我学到了更多关于 Promises 的知识! - Jim Bollinger
1
@codesections:“它们都依赖于在不同线程之间使用变量。”我曾认为INIT阶段不会(也不能)产生一个(非Nil)的值,直到封闭的compunit的运行阶段已经开始(并假定代码将在主/MAIN compunit中)。我甚至进行了各种测试来试图反驳我的想法,但是我没有检查编译器的代码。您可能已经这样做了,或者您知道我所不知道的东西。无论如何,如果您在我的答案下面解释一下,对于以后的读者来说可能会更好(然后我会删除这个评论)。 - raiph
@raiph,我其实不确定你提出的观点。出于你所说的原因,它可能是完全安全的。但我知道涉及到多个线程(请参见https://tio.run/##K0gtyjH7/9/J1d3TT6G4JLGoRKEotSA1sUShWqE4sVJBRSvEI8jV0cVaoTgnNbVAwUDP2FihVqE0ryQzR8HTzzMEqk4pMy@zxAquXMlaIaSoNFWhlosLajRYt7E1FxeKqf//AwA)。正如我所说,如果没有并发安全包装器跨线程共享数据会让我感到紧张 - 也就是说,即使它没问题,我也不想分散注意力去证明这一点。 - codesections
我想我对“它们都依赖于在不同线程之间使用变量”感到非常困惑。我在Liz的回答中看到了一个,但没有在我的回答中看到。我想也许你是在谈论Rakudo中涉及到它使用的某个变量的错误,也许作为某个并行管道compunit编译过程的一部分,这个错误非常严重,以至于Rakudo在完成编译之前就开始运行程序。因此,在编译时和运行时之间不仅存在重叠(这是可以理解的),而且还存在编译阶段和运行阶段之间的重叠。那将是一个巨大的错误。但我推断这一定是你的意思。是吗? - raiph
@codesections 这使得Marvin the Android感到意识到。"在另一个线程中产生的值(结束循环)"。在我的解决方案中,生成代码/值只是True。"这可能保证是线程安全的"如果True不能保证线程安全,那么没有什么可以保证了!这有意义吗?FYI Liz和你的解决方案数据竞争--.keep会打败~~Kept吗?如果.keep失败,程序开始运行后将显示一个额外的*。这不是什么大问题,但是想要赢得我在Liz中看到的比赛就是驱使我去解决这个问题的原因--据我所知,它总是能够赢得比赛。 - raiph
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接