如何在 Perl 中自动释放 RAII 风格的资源?

发布于 2024-08-26 06:02:51 字数 876 浏览 5 评论 0原文

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

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
process($fh);
close $fh or die "Couldn't close filename: $!";

假设进程可能会死亡。然后代码块提前退出,并且 $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 方法可以确保即使在出现异常的情况下也能像这样自动释放资源?或者显式错误检查是唯一的选择吗?

Say I have a resource (e.g. a filehandle or network socket) which has to be freed:

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
process($fh);
close $fh or die "Couldn't close filename: $!";

Suppose that process might die. Then the code block exits early, and $fh doesn't get closed.

I could explicitly check for errors:

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;

but this kind of code is notoriously difficult to get right, and only gets more complicated when you add more resources.

In C++ I would use RAII to create an object which owns the resource, and whose destructor would free it. That way, I don't have to remember to free the resource, and resource cleanup happens correctly as soon as the RAII object goes out of scope - even if an exception is thrown. Unfortunately in Perl a DESTROY method is unsuitable for this purpose as there are no guarantees for when it will be called.

Is there a Perlish way to ensure resources are automatically freed like this even in the presence of exceptions? Or is explicit error checking the only option?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

埖埖迣鎅 2024-09-02 06:02:51

我认为这就是 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::ScopeSub::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" }

I think that's what Scope::Guard was designed to help with.

#!/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" }

However, as @Philip notes in the comments, Scope::Guard utilizes the DESTROY method which creates some uncertainty as to when the scope exit code will be run. Modules such as Hook::Scope and Sub::ScopeFinalizer look fine as well although I have never used them.

I do like Try::Tiny for its clean interface and sheer simplicity and it will help you handle exceptions the correct way:

#!/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" }
苏佲洛 2024-09-02 06:02:51

我的模块 Scope::OnExit 正是用于此目的。

My module Scope::OnExit is intended for exactly that.

要走就滚别墨迹 2024-09-02 06:02:51

词法文件句柄的好处是,当它们超出范围时,它们会被关闭(并释放)。所以你可以这样做:

{
    # 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.

The nice thing about lexical filehandles is that they'll get closed (and freed) when they go out of scope. So you can just do something like this:

{
    # 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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文