共享电源可以同时运行多个分接块吗?

时间:2018-03-28 16:18:17

标签: perl6 tap raku

考虑这段代码需要一段时间才能完成。所有块同时运行(立即输出)然后休眠。大多数人都没有完成,因为程序结束的时间很快就会结束:

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

2 个答案:

答案 0 :(得分:13)

Supply的基本规则是:

  1. 如果没有明确要求
  2. ,就不会引入并发性
  3. 通过发送者支付模式的背压
  4. 在下一个消息之前完整处理消息(因此可以信任.map({ ...something with state... })不会导致状态冲突)
  5. 规则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阻止其他传入消息来实现这一点。

    最后,我要注意,Supplysupply结构需要考虑提供一些语言级机制来处理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

目前答案是:没有