异步读取标准输出

fin*_*fin 13 raku

我编写了这个简单的脚本,它每秒生成一个输出行(generator.sh):

\n
for i in {0..5}; do echo $i; sleep 1; done\n
Run Code Online (Sandbox Code Playgroud)\n

raku 程序将启动此脚本并在出现这些行时立即打印它们:

\n
my $proc = Proc::Async.new("sh", "generator.sh");\n$proc.stdout.tap({ .print });\nmy $promise = $proc.start;\nawait\xc2\xa0$promise;\n
Run Code Online (Sandbox Code Playgroud)\n

一切都按预期进行:每一秒我们都会看到一条新线。但是让我们用 raku 重写生成器(generator.raku):

\n
for 0..5 { .say; sleep 1 }\n
Run Code Online (Sandbox Code Playgroud)\n

并将程序的第一行更改为:

\n
my $proc = Proc::Async.new("raku", "generator.raku");\n
Run Code Online (Sandbox Code Playgroud)\n

现在出了点问题:首先我们看到输出的第一行(“0”),然后长时间停顿,最后我们看到输出的所有剩余行。

\n

我尝试通过命令获取生成器的输出script

\n
script -c \'sh generator.sh\' script-sh\nscript -c \'raku generator.raku\' script-raku\n
Run Code Online (Sandbox Code Playgroud)\n

在十六进制编辑器中分析它们,看起来它们是相同的:在每个数字之后,字节0d0a后面。

\n

为什么使用看似相同的发电机会产生如此大的差异?我需要理解这一点,因为我将启动一个外部程序并在线处理其输出。

\n

Jon*_*ton 19

为什么使用看似相同的发电机会产生如此大的差异?

首先,关于标题,问题不在于阅读方面,而在于写作方面。

Raku 的 I/O 实现会检查 STDOUT 是否附加到TTY。如果是 TTY,任何输出都会立即写入输出句柄。但是,如果它不是 TTY,那么它将应用缓冲,这会显着提高性能,但代价是输出被缓冲区大小分块。

如果更改generator.raku为禁用输出缓冲:

$*OUT.out-buffer = False; for 0..5 { .say; sleep 1 }
Run Code Online (Sandbox Code Playgroud)

然后立即可以看到输出。

我需要理解这一点,因为我将启动一个外部程序并在线处理其输出。

只有当您启动的外部程序也有这样的缓冲策略时,这才会成为一个问题。