你做错了什么是使用字符串“F”作为文件句柄。这 从来没有这样的工作;您可以使用裸号作为 文件句柄(open FH, ...; print FH ...
),或者您可以传入 空标量,并且perl会将新的打开文件对象分配给该 变量。但是如果你传入字符串F,那么你需要参考 然后处理为F
,而不是$fh
。但是,不要这样做。
而是执行此操作:
sub open_yaml_with_lock {
my ($file) = @_;
open my $fh, '+<', $file or die $!;
flock($fh, LOCK_EX) or die $!;
my $obj = YAML::Syck::LoadFile($fh); # this dies on failure
return ($obj, $fh);
}
我们在这里做的几件事情。其中一个,我们没有将 文件句柄存储在全局文件夹中。全球化状态使得您的程序极其难以理解 - 我的10条线路难度很大 - 应该避免。只要返回文件句柄,如果你想 保持它。或者,你可以像open
做它的别名:
sub open_yaml_with_lock {
open $_[0], '+<', $_[1] or die $!;
...
}
open_yaml_with_lock(my $fh, 'filename');
write_yaml_with_lock($fh);
不过说真的,这是一个烂摊子。把这东西放在一个对象中。使new
打开并锁定文件。添加一个write
方法。完成。现在你可以用 重用这段代码(并且让其他人也这样做),而不必担心 出错了。更少的压力。
我们在这里做的另一件事是检查错误。是的,磁盘可能会失败 。文件可能会错字。如果你乐于忽视开放和群体的返回值 ,那么你的程序可能不会做你认为 它正在做的事情。该文件可能无法打开。该文件可能不是 正确锁定。有一天,你的程序不能正常工作 因为你拼写“文件”为“flie”,并且文件无法打开。 你会想起正在发生的事情,让自己头脑发热好几个小时。最终,你会放弃,回家,然后再试。这一次, 你不会错过文件名,它会起作用。几个小时将浪费 。由于累积的压力,你会比你应该早几年死去。所以只需use autodie
或在您的系统调用后编写or die $!
,以便在出现错误时出现错误消息 !
如果您在顶部写了use autodie qw/open flock seek close/
,那么您的脚本将是正确的。 (其实,你也应该检查 “打印” 加工或使用 File::Slurp或 syswrite
,因为autodie无法检测失败print
声明。)
所以无论如何,概括地说:
当定义$fh
时,不要open $fh
。请将open my $fh
写入 避免考虑此问题。
总是检查系统调用的返回值。让自动拨号做 这个给你。
请勿保持全局状态。不要写一堆功能,这些功能可以一起使用,但像打开的文件一样依赖隐式前提条件 。如果函数具有先决条件,则将它们放在一个类中,并使构造函数满足前提条件。 这样,你不会不小心写出错误的代码!
更新
OK,这里是如何使这更OO。首先,我们将执行“纯Perl”OO ,然后使用Moose。驼鹿是 什么我会用于任何真正的工作; “纯Perl”只是为了让人们易于理解OO和 Perl的新人。
package LockedYAML;
use strict;
use warnings;
use Fcntl ':flock', 'SEEK_SET';
use YAML::Syck;
use autodie qw/open flock sysseek syswrite/;
sub new {
my ($class, $filename) = @_;
open my $fh, '+<', $filename;
flock $fh, LOCK_EX;
my $self = { obj => YAML::Syck::LoadFile($fh), fh => $fh };
bless $self, $class;
return $self;
}
sub object { $_[0]->{obj} }
sub write {
my ($self, $obj) = @_;
my $yaml = YAML::Syck::Dump($obj);
local $YAML::Syck::ImplicitUnicode = 1; # ensure that this is
# set for us only
my $fh = $self->{fh};
# use system seek/write to ensure this really does what we
# mean. optional.
sysseek $fh, 0, SEEK_SET;
syswrite $fh, $yaml;
$self->{obj} = $obj; # to keep things consistent
}
然后,我们可以使用类在我们的主要程序:
use LockedYAML;
my $resource = LockedYAML->new('filename');
print "Our object looks like: ". Dumper($resource->object);
$resource->write({ new => 'stuff' });
错误会抛出异常,它可以与 Try::Tiny进行处理,而YAML 文件就会一直为锁定该实例存在。你可以在 当然,一次有许多LockedYAML对象,这就是为什么我们 使它OO。
最后,驼鹿版本:
package LockedYAML;
use Moose;
use autodie qw/flock sysseek syswrite/;
use MooseX::Types::Path::Class qw(File);
has 'file' => (
is => 'ro',
isa => File,
handles => ['open'],
required => 1,
coerce => 1,
);
has 'fh' => (
is => 'ro',
isa => 'GlobRef',
lazy_build => 1,
);
has 'obj' => (
is => 'rw',
isa => 'HashRef', # or ArrayRef or ArrayRef|HashRef, or whatever
lazy_build => 1,
trigger => sub { shift->_update_obj(@_) },
);
sub _build_fh {
my $self = shift;
my $fh = $self->open('rw');
flock $fh, LOCK_EX;
return $fh;
}
sub _build_obj {
my $self = shift;
return YAML::Syck::LoadFile($self->fh);
}
sub _update_obj {
my ($self, $new, $old) = @_;
return unless $old; # only run if we are replacing something
my $yaml = YAML::Syck::Dump($new);
local $YAML::Syck::ImplicitUnicode = 1;
my $fh = $self->fh;
sysseek $fh, 0, SEEK_SET;
syswrite $fh, $yaml;
return;
}
这也同样应用于:
use LockedYAML;
my $resource = LockedYAML->new(file => 'filename');
$resource->obj; # the object
$resource->obj({ new => 'object' }); # automatically saved to disk
驼鹿版本是更长的时间,但可以做更多的运行时间一致性 检查和更容易提高。因人而异。
真不可思议!它的作品=)我真的从你的文章中学到了很多东西!我不知道我可以做'return($ obj,$ fh);''并打开我的$ fh'。我故意删除了错误处理,以使脚本缩短为post =)我使用Log4Perl的'$ logger-> error_die()'。但我不知道如何将它变成一个对象。你会怎么做? –
@Sandra Schlichting:已更新。 – jrockway
另外,也许你真的想要KiokuDB和文件后端,而不是这个锁定的YAML文件。 KiokuDB会将数据存储为YAML文件,但也会执行事务,因此您不必执行排它锁定。看看search.cpan。 – jrockway