4

假设我有一个必须释放的资源(例如文件句柄或网络套接字):

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 方法可以确保即使在存在异常的情况下也能像这样自动释放资源?还是显式错误检查是唯一的选择?

4

3 回答 3

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" }
于 2010-03-23T16:40:56.803 回答
4

我的模块Scope::OnExit正是为此而设计的。

于 2010-03-23T18:14:07.930 回答
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.
于 2010-03-23T16:45:29.707 回答