假设我有一个必须释放的资源(例如文件句柄或网络套接字):
open my $fh, "<", "filename" or die "Couldn't open filename: $!";
process($fh);
close $fh or die "Couldn't close filename: $!";
假设process
可能会死亡。然后代码块提前退出,$fh
不会关闭。
我可以明确检查错误:
open my $fh, "<", "filename" or die "Couldn't open filename: $!";
eval {process($fh)};
my $saved_error = $@;
close $fh or die "Couldn't close filename: $!";
die $saved_error if $saved_error;
但是这种代码很难做到正确,只有在添加更多资源时才会变得更加复杂。
在C ++中,我会使用RAII创建一个拥有该资源的对象,并且其析构函数将释放它。这样,我不必记住释放资源,并且一旦RAII对象超出范围,资源清理就会正确发生 - 即使抛出异常。不幸的是,在Perl中,DESTROY
方法不适用于此目的,因为无法保证何时调用它。
是否存在Perlish方法以确保即使在存在异常情况下资源也会自动释放?或者是显式错误检查唯一选项?
答案 0 :(得分:4)
我认为这是Scope::Guard旨在帮助的目的。
#!/usr/bin/perl
use strict; use warnings;
use Scope::Guard;
my $filename = 'file.test';
open my $fh, '>', $filename
or die "Couldn't open '$filename': $!";
{
my $sg = Scope::Guard->new(
sub {
close $fh or die "Could not close";
warn "file closed properly\n";
}
);
process($fh);
}
sub process { die "cannot process\n" }
但是,正如@Philip在评论中指出的那样,Scope::Guard
使用了DESTROY
方法,这会产生一些关于何时运行范围退出代码的不确定性。 Hook::Scope
和Sub::ScopeFinalizer
之类的模块看起来也很好,尽管我从未使用它们。
我喜欢Try::Tiny因为它干净的界面和纯粹的简洁性,它将帮助您以正确的方式处理异常:
#!/usr/bin/perl
use strict; use warnings;
use Try::Tiny;
my $filename = 'file.test';
open my $fh, '>', $filename
or die "Couldn't open '$filename': $!";
try {
process($fh);
}
catch {
warn $_;
}
finally {
close $fh
and warn "file closed properly\n";
};
sub process { die "cannot process\n" }
答案 1 :(得分:4)
我的模块Scope::OnExit就是为了这个目的。
答案 2 :(得分:3)
关于词法文件句柄的好处是,当它们超出范围时,它们将被关闭(并释放)。所以你可以这样做:
{
# bare block creates new scope
open my $fh, "<", "filename" or die "Couldn't open filename: $!";
eval { process($fh) };
# handle exceptions here
close $fh or die "Couldn't close filename: $!";
}
# $fh is now out of scope and goes away automagically.