Raku中从whenever块内部修改监听Supply目标不生效问题咨询
问题行为定性
该行为完全符合Raku的语言规范。
核心语义误解
你对whenever关键字的语义存在认知偏差:whenever $supply { ... } 仅会对传入的$supply表达式求值一次,拿到对应的Supply/Channel实例后就会直接订阅该实例,后续你修改存储实例的变量$currently-listening-to,不会改变已经建立的订阅关系,所以会持续收到$c1的消息。而$other-var是普通可变变量,修改它自然会生效,这就是两者表现不一致的原因。
该逻辑对supply块、react块、Channel场景通用,所以你在多个并发结构下都复现了相同行为。
动态切换监听源的实现方案
要实现可控的源切换,你需要手动管理订阅生命周期,切换时关闭旧订阅,再为新Supply建立新订阅即可,参考实现如下:
my $c1 = Supplier.new; my $c2 = Supplier.new; my $s = supply { my $active-tap; my $other-var = 'foo'; # 初始订阅c1 $active-tap = whenever $c1.Supply -> $msg { say "got: $msg"; if $msg.starts-with('3') { say "listening to something new"; # 关闭旧的c1订阅 $active-tap.close; $other-var = 'bar'; say $other-var; # 订阅新的c2 $active-tap = whenever $c2.Supply -> $new-msg { say "got: $new-msg"; # 可在此处添加自定义切换逻辑、错误处理逻辑,可控性远高于内置的migrate } } } } $s.tap; for ^7 { $c1.emit: "$_ from \$c1"; $c2.emit: "$_ from \$c2"; } sleep 1;
运行上述代码即可符合你的预期:收到3 from $c1之后,就会开始接收$c2的后续消息。
适配监管场景的方案
针对你要实现的Erlang风格进程监管逻辑,可以直接复用上述思路:将每个子进程对应的Supply和订阅Tap都存入状态变量,当监听到子进程异常消息时,先调用close关掉异常子进程的订阅,杀死异常子进程,启动新子进程后再为新子进程的Supply建立whenever订阅即可,完全可以满足动态监听新子进程的需求。
内容的提问来源于stack exchange,提问作者codesections
相关产品推荐
相关产品推荐

