raku - 为什么我的 promise (启动 block )中的所有 shell 进程都没有运行? (这是一个错误吗?)

标签 raku

我想运行多个 shell 进程,但是当我尝试运行超过 63 个时,它们会挂起。当我将线程池中的 max_threads 减少到 n 时,它在运行 nth shell 命令后挂起。

正如您在下面的代码中看到的,问题不在于 start block 本身,而在于包含 shellstart block > 命令:

#!/bin/env perl6
my $*SCHEDULER = ThreadPoolScheduler.new( max_threads => 2 );

my @processes;

# The Promises generated by this loop work as expected when awaited
for @*ARGS -> $item {
    @processes.append(
        start { say "Planning on processing $item" }
    );
}

# The nth Promise generated by the following loop hangs when awaited (where n = max_thread)
for @*ARGS -> $item {
    @processes.append(
        start { shell "echo 'processing $item'" }
    );
}
await(@processes);

运行 ./process_items foo bar baz 给出以下输出,卡在 processing bar 之后,它就在 n 之后第 (此处为 2nd)线程已使用 shell 运行:

Planning on processing foo
Planning on processing bar
Planning on processing baz
processing foo
processing bar

我做错了什么?或者这是一个错误?

在 CentOS 7 上测试的 Perl 6 发行版:
落语之星2018.06
乐道之星2018.10
乐道之星2019.03-RC2
落语之星2019.03

对于 Rakudo Star 2019.03-RC2,使用 v6.c使用 v6.d 没有任何区别。

最佳答案

shellrun 子程序使用Proc,它是根据Proc::Async 实现的。这在内部使用了线程池。通过对 shell 的阻塞调用填满池,线程池会耗尽,因此无法处理事件,从而导致挂起。

直接使用 Proc::Async 来完成这个任务会好得多。使用 shell 和大量真实线程的方法无法很好地扩展;每个 OS 线程都有内存开销、GC 开销等等。因为生成一堆子进程不受 CPU 限制,所以这是相当浪费的;实际上,只需要一两个真正的线程。因此,在这种情况下,也许在做一些低效的事情时让你退缩并不是最糟糕的事情。

我注意到使用 shell 和线程池的原因之一是试图限制并发进程的数量。但这不是一种非常可靠的方法;仅仅因为当前的线程池实现设置了默认的最大 64 个线程并不意味着它总是会这样做。

这是一个并行测试运行程序的示例,它一次最多运行 4 个进程,收集它们的输出并将其封装。它可能比您需要的多一点,但它很好地说明了整体解决方案的形状:

my $degree = 4;
my @tests = dir('t').grep(/\.t$/);
react {
    sub run-one {
        my $test = @tests.shift // return;
        my $proc = Proc::Async.new('perl6', '-Ilib', $test);
        my @output = "FILE: $test";
        whenever $proc.stdout.lines {
            push @output, "OUT: $_";
        }
        whenever $proc.stderr.lines {
            push @output, "ERR: $_";
        }
        my $finished = $proc.start;
        whenever $finished {
            push @output, "EXIT: {.exitcode}";
            say @output.join("\n");
            run-one();
        }
    }
    run-one for 1..$degree;
}

这里的关键是当一个进程结束时调用run-one,这意味着你总是用一个新的进程替换一个退出的进程,维护——只要有事情要做- 最多同时运行 4 个进程。当所有进程完成时,react block 自然结束,因为订阅的事件数降为零。

关于raku - 为什么我的 promise (启动 block )中的所有 shell 进程都没有运行? (这是一个错误吗?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59035286/

相关文章:

unicode - 如何在 Perl6 中按名称搜索 Unicode 代码点?

raku - 创建任意分组的子列表

terminal - 如何使用 perl6/rakudo 获取终端大小?

json - perl6 类到 json 排除属性

asynchronous - perl6 "An operation first awaited"

blocking - ncurses:为什么 getch 不等到我按下一个键?

hash - perl6 哈希键 <$/[0]> 、 <"$/[0]"> 和 {"$/[0]"} 使值的行为不同

signals - 如何在 Perl 6 中向进程 ID 发送信号?

capture - 像 Slurpy 一样使用 Capture

raku - 编写属性特征