perl6“首先等待的行动”

时间:2018-05-28 21:29:33

标签: asynchronous channel perl6

该程序创建一个线程,用dir()读取目录并将文件放在通道上。 $ N工作线程读取该通道并“处理”(打印)文件。

但是我得到这个“等待的操作:”错误。

我已多次阅读有关此错误的陷阱页面,但仍然无法理解它。有人能解释一下这里发生了什么吗?

目录内容:

$ ls
a  b  c  traverse-dir0.p6

运行程序:

$ ./traverse-dir0.p6 
traverse-dir0.p6
a
b
c
An operation first awaited:
  in sub MAIN at ./traverse-dir0.p6 line 24
  in block  at ./traverse-dir0.p6 line 5

Died with the exception:
    Cannot find method 'path': no method cache and no .^find_method
      in block  at ./traverse-dir0.p6 line 16

程序遍历-dir0.p6:

#!/usr/bin/env perl6
# There is a thread to populate $dir-channel by reading filenames in a directory with dir()
# and $N worker threads to read the filenames from the $dir-channel.

sub MAIN( Str $dir = ".", Int :$N = 4 ) {

    my $dir-channel = Channel.new();
    my $dir-read = start {
        $dir-channel.send( $_ ) for dir $dir;
        $dir-channel.close;
    }

    my @workers = (^$N).map: {
        start {
            while my $file = $dir-channel.receive() {
                say $file.path;
            }
            CATCH {
                when X::Channel::ReceiveOnClosed { .resume }
            }
        }
    }

    await $dir-read, @workers;
}

1 个答案:

答案 0 :(得分:8)

首先,关于从await抛出的异常的输出。异步操作失败时,有两条有趣的信息:

  • 在程序中我们想要操作的结果
  • 程序中出现问题意味着无法执行操作

第一条信息指示await的位置,堆栈跟踪与此相关。第二部分是关于await重新抛出异常的原因,并指出需要修复的问题。

在这种情况下的问题是在没有一个对象的对象上调用path方法。这要归功于.resume,这没有任何意义。抛出异常是说无法从通道接收值。恢复它只是意味着循环体在$file中以未定义的值运行,缺少path方法并导致错误。 (顺便说一下:.resume是正确答案的非常非常罕见。)

对代码的最小修复是将.resume替换为last,这会在关闭频道时终止迭代:

my @workers = (^$N).map: {
    start {
        while my $file = $dir-channel.receive() {
            say $file.path;
            CATCH {
                when X::Channel::ReceiveOnClosed { last }
            }
        }
    }
}

但是,将Channel强制转换为可迭代的Seq要简单得多。当Channel关闭时,这会自动处理终止迭代,所以不会有例外情况:

my @workers = (^$N).map: {
    start {
        for $dir-channel.Seq -> $file {
            say $file.path;
        }
    }
}

由于start是一个语句前缀,因此会进一步缩短为:

my @workers = (^$N).map: {
    start for $dir-channel.Seq -> $file {
        say $file.path;
    }   
}   

我很欣赏这可能是一个更有趣的问题的简化版本,或者可能是为了探索各种Perl 6并发概念,但整个版本可以替换为:

sub MAIN( Str $dir = ".", Int :$N = 4 ) {
    race for dir($dir).race(batch => 1, degree => $N) -> $file {
        say $file.path;
    }
}

具有相同的语义,但可以节省启动和管理工作人员,同时仍然控制工作人员的数量,并确保文件在工作人员中以相同的方式分发。