【问题标题】:Perl6: Cannot invoke this object (REPR: P6opaque; Parallel::ForkManager)Perl6:无法调用此对象(REPR:P6opaque;Parallel::ForkManager)
【发布时间】:2019-08-12 11:40:44
【问题描述】:

我正在尝试使用 Perl5 的 Parallel::ForkManager 在 Perl6 中并行运行一系列 shell 命令 这是对工作 Perl5 代码的几乎完全准确的翻译。

CONTROL {
    when CX::Warn {
        note $_;
        exit 1;
    }
}
use fatal;
role KeyRequired {
    method AT-KEY (\key) {
        die "Key {key} not found" unless self.EXISTS-KEY(key);
        nextsame;
    }
}

use Parallel::ForkManager:from<Perl5>;

sub run_parallel (@cmd) {
    my $manager = Parallel::ForkManager(8).new();
    for (@cmd) -> $command  {
        $manager.start and $manager.next;
        my $proc = shell $command, :out, :err;
        if $proc.exitcode != 0 {
            put "$command failed";
            put $proc.out.slurp;
            put $proc.err.slurp;
            die;
        }
        $manager.finish;
    }
    $manager.wait_all_children;#necessary after all lists
}

my @cmd;
my Str $dir = 'A/1';
for dir($dir, test => /\.vcf\.gz$/) -> $vcf {
    @cmd.append: "aws s3 cp $vcf s3://s3dir/$dir/"
}
put @cmd.elems;
run_parallel(@cmd);

基本上,我正在尝试并行化繁琐的 shell 命令。

然而,这个神秘的错误出现了:

无法调用此对象 (REPR: P6opaque; Parallel::ForkManager) sub run_parallel at 2.aws_cp.p6 line 18 in block at 2.aws_cp.p6第39行

为什么 Perl6 会这样说?怎么了?如何让这些命令运行?

也许在 Perl6 中有一种更原生/惯用的方式来并行运行 shell 命令?

【问题讨论】:

  • 我真的不建议尝试在 Perl6 中使用 Perl5 分叉代码,因为它有一个完全不同的管理器异步和并行代码系统。
  • "forking" 是它试图做的正确术语,即并行运行 shell 命令? @Scimon
  • 我明白了。我的观点是 Perl6 不使用 Forking 进行并行工作,而是使用线程。
  • 其他人指出了为什么他们认为 P5 的 Parallel::ForkManager 似乎不太可能与 P6 一起使用。我对此一无所知。以下内容完全独立于此,而是以涉及线程的方式将 P6 与 P5 一起使用。根据the BUILDING section of the Inline::Perl5 README,“如果您想安全地使用多个Inline::Perl5 interpeter,例如在Perl 6 线程中,请添加-Dusemultiplicity 选项”。

标签: raku


【解决方案1】:

您可能想看看使用Proc::Async,它在线程中异步运行外部命令,而无需派生单独的代码实例来执行此操作。

【讨论】:

  • 甚至可以试试 Proc::Q 模块,它适用于需要并行运行大量进程的情况。
【解决方案2】:

Perl5 的 Parallel::ForkManager 可能无法在 Perl6 中工作,因为 Inline::Perl5 是如何实现的。

Inline::Perl5 将 Perl5 编译器/运行时嵌入到 Perl6 中。

Parallel::ForkManager 认为 Perl5 是自己运行的。

如果你真的让它做一些除了产生错误之外的事情,它可能会搞砸 Perl6 运行时。主要问题是fork的使用。有关为什么 fork 是一个问题的更多信息,请参阅 Bart Wiegmans (brrt) 写的文章:“A future for fork(2)”


Perl6 已经有一个类似的功能更易于使用。

sub run_parallel (@cmd) {
    my @children = do for (@cmd) -> $command  {
        start {
            my $proc = shell $command, :out, :err;
            if $proc.exitcode != 0 {
                put "$command failed";
                put $proc.out.slurp;
                put $proc.err.slurp;
                die;
            }
        }
    }
    await @children;
}

start 是一个前缀,它告诉运行时在不久的将来某个时间开始运行以下代码。它返回一个 Promise
await 采用 Promise 的列表并返回它们的结果列表。

start 基本上调用Promise.start 类似于:

sub start ( &code ) {
    my $promise = Promise.new;
    my $vow = $promise.vow;

    $*SCHEDULER.cue(
        { $vow.keep(code(|c)) },
        :catch(-> $ex { $vow.break($ex); }) );
    $promise
}

因此它将使用$*SCHEDULER 中的全局可用线程池。如果你想使用一个单独的,你可以。

sub run_parallel (@cmd) {
    my $*SCHEDULER = ThreadPoolScheduler.new(max_threads => 8);

    my @children = do for (@cmd) -> $command  {
        start {
            my $proc = shell $command, :out, :err;
            if $proc.exitcode != 0 {
                put "$command failed";
                put $proc.out.slurp;
                put $proc.err.slurp;
                die;
            }
        }
    }
    await @children;
}

不过,使用 Proc::Async 会更有意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-26
    • 2018-12-09
    • 1970-01-01
    • 2013-04-16
    • 2017-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多