首页 > 解决方案 > 每当信号()在反应块顺序依赖?

问题描述

我有一个小程序,它一直运行到收到 SIGINT 或收到来自 stdin 的两行(按两次回车)。反应块逻辑是:

react {
    whenever signal(SIGINT) {
        say "Got signal";
        exit;
    }
    whenever $*IN.lines.Supply {
        say "Got line";
        exit if $++ == 1 ;
    }
}

程序将按预期在两个输入的行上退出。

然而 CTRL-C 不会做任何事情,除非它后面跟着一行(回车)。

如果我切换任何时候块的顺序,程序会被 SIGINT 中断,但不会在任何时候执行信号块

react {
    whenever $*IN.lines.Supply {
        say "Got line";
        exit if $++ == 1 ;
    }
    whenever signal(SIGINT) {
        say "Got signal";
        exit;
    }
}

在使用信号子之前是否需要进行一些其他设置?任何时候块的顺序在反应块中重要吗?

更新

所以似乎 lines() 调用阻止了 react 块的执行(感谢@Håkon)。我有点明白。

当与读取​​套接字的类似代码结构进行比较时,我很困惑。数据的存在(或缺乏)对信号处理程序的执行没有影响,在这个例子中它可以很好地读取行:

my $listener=IO::Socket::Async.listen("0.0.0.0",4432);
react {
    whenever $listener {
        whenever $_.Supply.lines() {
            say "Got line";
        }
    }
    whenever signal(SIGINT) {
        say "Got signal";
        exit;
    }
}

#testing with:
#   curl http://localhost:4432

为什么这与我的原始代码如此不同?

标签: raku

解决方案


如果数据源确实以异步方式运行,则顺序无关紧要,不幸的是这里不是这种情况。Supplya 上的强制Seq器不会引入任何并发性,并且会立即尝试生成一个值以在 上发出Supply,这反过来又会阻止读取$*IN. 因此,第二次订阅没有机会建立;相同的根本问题导致观察到的其他问题。

解决方案是强制阅读发生在“其他地方”。我们可以用 来做到这一点Supply.from-list(...),并告诉它我们确实想要使用当前调度程序而不是它的默认值CurrentThreadScheduler。因此,这表现得如愿:

react {
    whenever Supply.from-list($*IN.lines, scheduler => $*SCHEDULER) {
        say "Got line";
        exit if $++ == 1 ;
    }
    whenever signal(SIGINT) {
        say "Got signal";
        exit;
    }
}

在未来的 Perl 6 版本中,这个区域很可能会有所修改。当前的行为是善意的;设计原则是避免隐式引入并发性,遵循供应是管理固有存在的并发性的工具的一般原则,而不是引入它。然而,实际上,这里缺乏并发性可能让更多的人绊倒,而不是帮助。(此外,我们可能会考虑提供真正的非阻塞文件 I/O,而不是从同步文件 I/O + 线程中构建它。)


推荐阅读