perl6 "An operation first awaited"
perl6 "An operation first awaited"
此程序创建一个线程以使用 dir() 读取目录并将文件放置在通道上。 $N 个工作线程读取该通道并 "process"(打印)文件。
但是我收到这个 "An operation first awaited:" 错误。
我已经多次阅读有关此错误的陷阱页面,但仍然无法理解。有人能解释一下这是怎么回事吗?
目录内容:
$ 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
程序traverse-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;
}
首先,关于 await
抛出异常的输出。异步操作失败时有两条有趣的信息:
- 在程序中我们想要操作结果的地方
- 程序中哪里出现问题导致无法执行操作
第一条信息指示 await
的位置,堆栈跟踪与此相关。第二部分是关于await
异常被重新抛出的原因,并指出需要修复的问题。
本例中的问题是 path
方法是在一个没有对象的对象上调用的。这要归功于 .resume
,这毫无意义。抛出异常表明无法从通道接收值。恢复它只是意味着循环体在 $file
中以未定义的值运行,它缺少 path
方法并导致错误。 (顺便说一句:.resume
是非常非常罕见的正确答案。)
代码的最小修复是用 last
替换 .resume
,当通道关闭时终止迭代:
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;
}
}
它具有相同的语义,但节省了启动和管理工作人员的时间,同时仍然控制工作人员的数量并确保文件以相同的方式在工作人员之间分发。
此程序创建一个线程以使用 dir() 读取目录并将文件放置在通道上。 $N 个工作线程读取该通道并 "process"(打印)文件。
但是我收到这个 "An operation first awaited:" 错误。
我已经多次阅读有关此错误的陷阱页面,但仍然无法理解。有人能解释一下这是怎么回事吗?
目录内容:
$ 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
程序traverse-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; }
首先,关于 await
抛出异常的输出。异步操作失败时有两条有趣的信息:
- 在程序中我们想要操作结果的地方
- 程序中哪里出现问题导致无法执行操作
第一条信息指示 await
的位置,堆栈跟踪与此相关。第二部分是关于await
异常被重新抛出的原因,并指出需要修复的问题。
本例中的问题是 path
方法是在一个没有对象的对象上调用的。这要归功于 .resume
,这毫无意义。抛出异常表明无法从通道接收值。恢复它只是意味着循环体在 $file
中以未定义的值运行,它缺少 path
方法并导致错误。 (顺便说一句:.resume
是非常非常罕见的正确答案。)
代码的最小修复是用 last
替换 .resume
,当通道关闭时终止迭代:
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;
}
}
它具有相同的语义,但节省了启动和管理工作人员的时间,同时仍然控制工作人员的数量并确保文件以相同的方式在工作人员之间分发。