检查反应块是否准备好用于业务
Checking if a react block is ready for business
编写并发代码时,很常见的情况是要分离一个单独的(绿色或 OS)线程,然后要求该线程中的代码对各种线程安全消息作出反应。 Raku 以多种方式支持这种模式。
例如,文档中的许多 Channel examples 显示的代码类似于下面的代码(跨两个线程打印 1 到 10)。
my $channel = Channel.new;
start { react whenever $channel { say $_ }}
for ^10 { $channel.send($_) }
sleep 1
但是,如果我们从 Channel
s 的单消费者世界切换到实时 Supply
s 的多消费者世界,等效代码将不再有效。
my Supplier $supplier .= new;
start { react whenever $supplier { say $_ }}
for ^10 { $supplier.emit($_) }
sleep 1;
此代码不打印任何内容。据我了解,这是因为 react
块在值被 emit
ed 时没有监听 - start
线程和 react
不需要很长时间事件,但 emit
十个值花费的时间更少。而且,从逻辑上讲,将 sleep 1
行移动到 for
循环上方会导致再次打印值。
这很公平——毕竟,使用实时 Supply
而不是点播的原因是因为您需要实时语义。也就是说,您只想 react
到未来的事件,而不是过去的事件。
但我的问题是是否有办法在线程中询问 react
块我已经 start
编辑它是否准备好 and/or 等待它之前准备好发送数据。 (await
ing start
块会等到线程 完成 ,而不是等到它准备好,所以这对这里没有帮助)。
我也乐于接受这样的回答,说我正在处理这个 incorrectly/there 的 X-Y 问题——我完全有可能违背语言试图推动我或那个的方向此处实时 Supply
不是正确的并发抽象。
对于这种特定情况(这是一个相对常见的情况),答案是使用 Supplier::Preserving
:
my Supplier::Preserving $supplier .= new;
start { react whenever $supplier { say $_ }}
for ^10 { $supplier.emit($_) }
sleep 1;
它保留发送的值直到 $supplier
被第一次点击,然后发出它们。
另一种更通用的解决方案是使用 Promise
:
my Supplier $supplier .= new;
# A Promise used just for synchronization
my Promise $ready .= new;
start react {
# Set up the subscriptions...
whenever $supplier { say $_ }
# ...and then signal that they are ready.
$ready.keep;
}
# Wait for the subscriptions to be set up...
await $ready;
# ...and off we go.
for ^10 { $supplier.emit($_) }
sleep 1;
react
块中的 whenever
在遇到时设置订阅,因此到 Promise
保留时,所有订阅都已完成。 (此外,虽然这里不重要,但在 react
块的主体完成所有设置之前不会处理任何消息。)
最后我会注意到,虽然 Supplier
经常被达到,但很多时候最好写一个 supply
块,其中 emit
是值。问题中的示例(相当合理)是从具体应用程序中抽象出来的,但几乎总是值得一问,“我可以通过编写 supply
块来做我想做的事情吗?”然后再达到 Supplier
或 Supplier::Preserving
。如果您确实需要广播值或需要将异步输入分发到多个位置,那么 Supplier
是个不错的选择;如果它只是一个单一的价值流,一旦被点击就会产生,那么可能没有。
编写并发代码时,很常见的情况是要分离一个单独的(绿色或 OS)线程,然后要求该线程中的代码对各种线程安全消息作出反应。 Raku 以多种方式支持这种模式。
例如,文档中的许多 Channel examples 显示的代码类似于下面的代码(跨两个线程打印 1 到 10)。
my $channel = Channel.new;
start { react whenever $channel { say $_ }}
for ^10 { $channel.send($_) }
sleep 1
但是,如果我们从 Channel
s 的单消费者世界切换到实时 Supply
s 的多消费者世界,等效代码将不再有效。
my Supplier $supplier .= new;
start { react whenever $supplier { say $_ }}
for ^10 { $supplier.emit($_) }
sleep 1;
此代码不打印任何内容。据我了解,这是因为 react
块在值被 emit
ed 时没有监听 - start
线程和 react
不需要很长时间事件,但 emit
十个值花费的时间更少。而且,从逻辑上讲,将 sleep 1
行移动到 for
循环上方会导致再次打印值。
这很公平——毕竟,使用实时 Supply
而不是点播的原因是因为您需要实时语义。也就是说,您只想 react
到未来的事件,而不是过去的事件。
但我的问题是是否有办法在线程中询问 react
块我已经 start
编辑它是否准备好 and/or 等待它之前准备好发送数据。 (await
ing start
块会等到线程 完成 ,而不是等到它准备好,所以这对这里没有帮助)。
我也乐于接受这样的回答,说我正在处理这个 incorrectly/there 的 X-Y 问题——我完全有可能违背语言试图推动我或那个的方向此处实时 Supply
不是正确的并发抽象。
对于这种特定情况(这是一个相对常见的情况),答案是使用 Supplier::Preserving
:
my Supplier::Preserving $supplier .= new;
start { react whenever $supplier { say $_ }}
for ^10 { $supplier.emit($_) }
sleep 1;
它保留发送的值直到 $supplier
被第一次点击,然后发出它们。
另一种更通用的解决方案是使用 Promise
:
my Supplier $supplier .= new;
# A Promise used just for synchronization
my Promise $ready .= new;
start react {
# Set up the subscriptions...
whenever $supplier { say $_ }
# ...and then signal that they are ready.
$ready.keep;
}
# Wait for the subscriptions to be set up...
await $ready;
# ...and off we go.
for ^10 { $supplier.emit($_) }
sleep 1;
react
块中的 whenever
在遇到时设置订阅,因此到 Promise
保留时,所有订阅都已完成。 (此外,虽然这里不重要,但在 react
块的主体完成所有设置之前不会处理任何消息。)
最后我会注意到,虽然 Supplier
经常被达到,但很多时候最好写一个 supply
块,其中 emit
是值。问题中的示例(相当合理)是从具体应用程序中抽象出来的,但几乎总是值得一问,“我可以通过编写 supply
块来做我想做的事情吗?”然后再达到 Supplier
或 Supplier::Preserving
。如果您确实需要广播值或需要将异步输入分发到多个位置,那么 Supplier
是个不错的选择;如果它只是一个单一的价值流,一旦被点击就会产生,那么可能没有。