perl6"首先等待的行动"

Nor*_*ood 6 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;
}

Jon*_*ton 8

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

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

第一条信息指示了该位置await,并且堆栈跟踪与之相关.第二部分是关于为什么异常被重新抛出await,并指出需要修复的问题.

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

对代码的最小修复是将a替换为.resumea last,当通道关闭时终止迭代:

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

但是,将其强制Channel转换为可迭代内容要简单得多Seq.这会在Channel关闭时自动处理终止迭代,因此不会出现异常:

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

因为start是一个语句前缀,它进一步缩短为:

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

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

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

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