该程序创建一个线程,用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; }
答案 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;
}
}
具有相同的语义,但可以节省启动和管理工作人员,同时仍然控制工作人员的数量,并确保文件在工作人员中以相同的方式分发。