检查反应块是否已准备好用于业务

cod*_*ons 10 concurrency message-passing thread-safety rakudo raku

在编写并发代码时,想要分离一个单独的(绿色或操作系统)线程,然后要求该线程中的代码对各种线程安全消息做出反应是相当常见的。Raku 通过多种方式支持这种模式。

\n

例如,文档中的许多Channel 示例显示的代码与下面的代码类似(跨两个线程打印 1 到 10)。

\n
my $channel = Channel.new;\nstart   { react whenever $channel { say $_ }}\nfor ^10 { $channel.send($_) }\nsleep 1\n
Run Code Online (Sandbox Code Playgroud)\n

但是,如果我们从 s 的单消费者世界切换Channel到 live s 的多消费者世界Supply,则等效代码不再有效。

\n
my Supplier $supplier .= new;\nstart   { react whenever $supplier { say $_ }}\nfor ^10 { $supplier.emit($_) }\nsleep 1;\n
Run Code Online (Sandbox Code Playgroud)\n

该代码不打印任何内容。据我了解,这是因为react当值被编辑 \xe2\x80\x93 时该块没有监听,emit它不会花费很长时间来处理start线程和react事件,但处理emit十个值所需的时间甚至更少。而且,从逻辑上讲,将行移到循环sleep 1上方for会导致再次打印值。

\n

毕竟,这很公平 \xe2\x80\x93,使用实时Supply而不是点播的原因是因为你想要实时语义。也就是说,你只想react未来的事件,而不是过去的事件。

\n

但我的问题是,是否有一种方法可以询问react我编辑的线程中的块start是否已准备好和/或在发送数据之前等待它准备好。( awaitingstart块等待线程完成而不是直到它准备好,所以这在这里没有帮助)。

\n

我也愿意回答说我处理这个问题的方法不正确/存在 XY 问题 \xe2\x80\x93 完全有可能我正在努力对抗语言正在尝试的方向推动我或者说 liveSupply在这里不是正确的并发抽象。

\n

Jon*_*ton 11

对于这种特定情况(这是一种相对常见的情况),答案是使用Supplier::Preserving:

my Supplier::Preserving $supplier .= new;
start   { react whenever $supplier { say $_ }}
for ^10 { $supplier.emit($_) }
sleep 1;
Run Code Online (Sandbox Code Playgroud)

它保留发送的值,直到$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;
Run Code Online (Sandbox Code Playgroud)

whenever块中的 s 在遇到react时设置订阅,因此在Promise保留 时,所有订阅都已完成。(此外,尽管这里并不重要,但在块的主体react完成所有设置之前不会处理任何消息。)

最后我要指出的是,虽然Supplier经常达到,但很多时候最好编写一个包含值的supply块。emit问题中的示例是(相当合理地)从具体应用程序中抽象出来的,但在达到 a或supply之前,几乎总是值得问“我可以通过编写一个块来完成我想要的事情吗” 。如果您确实需要广播值或需要将异步输入分发到多个位置,那么有一个可靠的例子;如果它只是在点击后生成一个值流,则可能不会。SupplierSupplier::PreservingSupplier