Raku等同于JavaScript的`setTimeout(fn,0)`?

JavaScript 的事件循环使用消息队列来安排工作,并在开始下一条消息之前将每条消息运行到完成。因此,JavaScript 代码中一个小众但令人惊讶的常见模式是安排一个函数在当前队列中的消息使用setTimeout(fn, 0). 例如:

setTimeout(() => {console.log('first')}, 0);
console.log('second'); 
// OUTPUT: "secondnfirst"

(有关更多详细信息,请参阅MDN 的描述。)

Raku's 是否提供任何类似的方法来在所有当前计划的工作完成后立即安排工作?根据我对 Raku 并发模型的理解(主要来自这篇 6guts 帖子),似乎 Raku 使用了类似的消息队列(尽管如果有误,请纠正我!)。我最初认为这Promise.in(0).then: &fn是一个直接的等价物:

my $p = Promise.in(0).then: { say 'first' }
say 'second';
await $p;
# OUTPUT: «secondnfirst» # ...usually

但是,在运行上面的代码很多次后,我意识到,这只是建立一个竞争条件和'first'有时第一。那么,是否有任何 Raku 代码提供相同的行为?而且,如果是这样,这种行为是否是 Raku/Roast 决定的有意语义的结果,而不是(可能是临时的)实现细节的结果?

回答

无序

Raku 没有有序的消息队列。它有一个需要做的事情的无序列表。

# schedule them to run at the same second
# just to make it more likely that they will be out of order
my $wait = now + 1;

my @run;
for 1..20 -> $n {
  push @run, Promise.at($wait).then: {say $n}
}
await @run;

这可以按任何顺序打印数字。

1
2
3
4
5
6
7
8
11
12
13
14
15
16
17
18
9
10
19
20

Raku 实际上是多线程的。如果你给它足够的工作,它将使用你所有的 CPU 内核。

这意味着在当前队列中的所有内容完成后,永远无法说运行它

即使我只是使用start,它有时也可能会出现乱序。

my @run;
for 1..20 -> $n {
    push @run, start {say $n}
};
await @run;

你可以在它开始乱序打印之前运行数百次,但它最终会这样做。

即使您进入低级并使用了$*SCHEDULER.cue,也不能保证它会在其他一切之后运行。

my @nums;
for 1..100 -> $n {
    $*SCHEDULER.cue: {push @nums, $n; say $n}
}
say @nums;

它不仅可能会乱序运行,而且@nums数组可能不会包含所有值,因为每个线程可能会破坏另一个线程正在执行的操作。

在幕后,安排某事运行的 Promise 方法最终$*SCHEDULER.cue以某种方式调用。

安排其他事情

你可以告诉 Raku 在其他事情之后运行你的代码。

my $p = Promise.in(1);
my $p2 = $p.then: {say 'first'}
my $p3 = $p.then: {say 'second'}
react {
  whenever start say('first') {
    whenever start say('second') {
    }
  }
}

不过,你需要参考那件事。

减缓

如果 Raku 确实有办法在当前计划的事件之后运行事物,那么它必须跟踪正在运行的内容并确保您的代码在它们完成后才运行。

my $a = start {

    # pointless busy-work that takes two seconds
    my $wait = now + 2;
    my $n = 0;
    while now ? $wait {
        $n++
    }
    say $n; # make sure the loop doesn't get optimized away

    say 'first';
}

my $b = start say 'second';

await $a, $b;
second
1427387
first

如果这确保$b在 之后运行$a,那么$b整整两秒内都不会进行任何工作。

相反,它只会导致$b在另一个线程上运行,因为正在处理的线程$a当前正忙。

这是一件好事,因为如果$b它也很慢。我们将安排两个缓慢的事情按顺序运行而不是并行运行。

Javascript

我认为它目前在 Javascript 中工作的唯一原因是因为它似乎没有利用多个 cpu 内核。或者它有类似 GIL 的东西。

我编写的 Raku 代码使我的 4 核 CPU 利用率为 500%。(英特尔超线程 CPU,其中一个内核似乎是 2 个内核)
我不确定您是否可以使用单个 Javascript 程序执行相同操作。


以上是Raku等同于JavaScript的`setTimeout(fn,0)`?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>