考虑这段代码需要一段时间才能完成。所有块同时运行(立即输出)然后休眠。大多数人都没有完成,因为程序结束的时间很快就会结束:
my $supply = Supply.interval(0.2);
my $tap = $supply.tap: { say "1 $^a"; sleep 5; };
sleep 5;
输出(省略)有25行(每个刻度为1,每5秒内为0.2):
1. 0
1. 1
...
1. 24
然后我将该供应更改为.share
:
my $supply = Supply.interval(0.2).share;
my $tap = $supply.tap: { say "1. $^a"; sleep 5 };
sleep 5;
我只看到一行输入,但我期望输出相同:
1. 1
.share
使多个分接头可以获得相同的值。
my $supply = Supply.interval(0.2).share;
my $tap = $supply.tap: { say "1. $^a"; sleep 5 };
my $tap2 = $supply.tap: { say "2. $^a"; };
sleep 5;
输出仍然仅为第一次点击输出,但仍然只有一行。我预计每行25行:
1. 1
答案 0 :(得分:13)
Supply
的基本规则是:
.map({ ...something with state... })
不会导致状态冲突)规则3并不真正适用于share
,因为在该点之后有单独的下游操作链,但规则1和规则2。 share
的目的是允许发布/订阅,并且还提供多个下游消息处理器的一大块处理的重用。引入并行消息处理是一个单独的问题。
有多种选择。一种是将并行处理的消息插入Channel
。这明确地为要缓冲的消息引入了一个位置(好吧,直到你的内存耗尽......这正是Supply
附带发送者支付背压模型的原因)。将Channel
强制转换回Supply
会从Channel
获取值,并在池线程上的Supply
上发出。那样看起来像:
my $supply = Supply.interval(0.2).share;
my $tap = $supply.Channel.Supply.tap: { say "1. $^a"; sleep 5 };
my $tap2 = $supply.tap: { say "2. $^a"; };
sleep 5;
请注意,由于whenever
会自动强制要求对Supply
做出反应,因此whenever $supply.Channel { }
看起来像Channel
,这使得它成为start
非常简短的解决方案 - 但同时非常清楚,它表明 正常的背压机制正在进行侧步。此解决方案的另一个属性是它保留了消息的顺序,并且仍然在Supply
的下游提供一次一个处理。
另一种方法是通过启动一些异步工作来处理它来对每条消息做出反应。 Supply
上的Supply
操作会调度传递的块以在线程池上为每个收到的消息运行,从而不会阻止下一条消息的到达。结果是Supply
react
。这迫使人们点击每个内部whenever
来实际发生任何事情,这在一开始似乎有点反直觉,但实际上是为了程序员的利益:它清楚地表明这里有一点额外的异步工作以跟踪。我强烈建议将其与my $supply = Supply.interval(0.2).share;
my $tap = supply { whenever $supply.start({ say "1. $^a"; sleep 5 }) { whenever $_ {} } }.tap;
my $tap2 = $supply.tap: { say "2. $^a"; };
sleep 5;
/ my $supply = Supply.interval(0.2).share;
my $tap = supply { whenever $supply -> $a { whenever start { say "1. $a"; sleep 5 } {} } }.tap;
my $tap2 = $supply.tap: { say "2. $^a"; };
sleep 5;
语法结合使用,该语法自动执行订阅管理和错误传播。问题中代码的最直接转换是:
parallelize
虽然也可以将其写成:
Supply
指出编写my $supply = Supply.interval(0.2).share;
my $tap = parallelize($supply, { say "1. $^a"; sleep 5 }).tap;
my $tap2 = $supply.tap: { say "2. $^a"; };
sleep 5;
sub parallelize(Supply $messages, &operation) {
supply {
whenever $messages -> $value {
whenever start operation($value) {
emit $_;
}
}
}
}
Channel
组合子的可能性:
Channel
这种方法的输出与Promises
方法的输出完全不同,因为一旦消息进入,操作就会全部启动。它也不会保留消息顺序。还有一个隐式队列(与使用await Promise.anyof(@outstanding)
方法的显式队列不同),现在只是它是线程池调度程序的工作队列和操作系统调度程序必须跟踪正在进行的工作。同样,没有背压,但请注意,完全有可能通过跟踪未完成的hyper whenever
并使用race whenever
阻止其他传入消息来实现这一点。
最后,我要注意,Supply
和supply
结构需要考虑提供一些语言级机制来处理EmName = "SELECT * FROM tbl_Order WHERE AssignedTo = " & Me.ComName & ""
消息的并行处理。然而,这种语义,以及它们如何影响{{1}} - 块设计目标和安全属性,代表了重大的设计挑战。
答案 1 :(得分:6)
Supply
的点击在单个线程中按顺序运行。因此,第二次点击的代码将仅在第一次点击(睡眠5秒)后运行。这显示在以下代码中:
my $supply = Supply.interval(0.2).share;
my $tap = $supply.tap: { say "1. $^a in #{+$*THREAD}" };
my $tap2 = $supply.tap: { say "2. $^a in #{+$*THREAD}" };
sleep 0.5;
===================
1. 1 in #4
2. 1 in #4
1. 2 in #4
2. 2 in #4
目前答案是:没有