当我运行下面的代码时,我得到了
Can't use string ("F") as a symbol ref while "strict refs" in use at ./T.pl line 21.
Run Code Online (Sandbox Code Playgroud)
第21行是
flock($fh, LOCK_EX);
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl ':flock', 'SEEK_SET'; # file locking
use Data::Dumper;
# use xx;
my $file = "T.yaml";
my $fh = "F";
my $obj = open_yaml_with_lock($file, $fh);
$obj->{a} = 1;
write_yaml_with_lock($obj, $fh);
sub open_yaml_with_lock {
my ($file, $fh) = @_;
open $fh, '+<', $file;
flock($fh, LOCK_EX);
my $obj = YAML::Syck::LoadFile($fh);
return $obj;
}
sub write_yaml_with_lock {
my ($obj, $fh) = @_;
my $yaml = YAML::Syck::Dump($obj);
$YAML::Syck::ImplicitUnicode = 1;
seek $fh,0, SEEK_SET; # seek back to the beginning of file
print $fh $yaml . "---\n";
close $fh;
}
Run Code Online (Sandbox Code Playgroud)
你做错了是使用字符串"F"作为文件句柄.这从来都不是有用的东西; 你可以使用一个裸字作为文件句柄(open FH, ...; print FH ...),或者你可以传入一个空标量,perl会为该变量分配一个新的打开文件对象.但是如果你传入字符串F,那么你需要引用然后处理as 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);
}
Run Code Online (Sandbox Code Playgroud)
我们在这里做了几件事.一,我们不是将文件句柄存储在全局中.全球状态使你的程序非常难以理解 - 我的10行帖子很难 - 应该避免.如果你想保留它,只需返回文件句柄即可.或者,你可以像它一样别名open:
sub open_yaml_with_lock {
open $_[0], '+<', $_[1] or die $!;
...
}
open_yaml_with_lock(my $fh, 'filename');
write_yaml_with_lock($fh);
Run Code Online (Sandbox Code Playgroud)
但实际上,这是一团糟.把这些东西放在一个物体里.请new
打开并锁定该文件.添加write方法.完成.现在您可以重用此代码(并让其他人也这样做),而不必担心出错.更少的压力.
我们在这里做的另一件事是检查错误.是的,磁盘可能会失败.文件可能被拼写错误.如果你幸福地忽略了open和flock的返回值,那么你的程序可能没有做你认为它正在做的事情.该文件可能无法打开.该文件可能未正确锁定.有一天,您的程序无法正常工作,因为您将"文件"拼写为"flie"并且无法打开该文件.几个小时你会抓挠头脑,想知道发生了什么.最终,你会放弃,回家,稍后再试.这一次,你不会错误地输入文件名,它会起作用.几个小时就会被浪费掉.由于累积的压力,你会比你应该早几年死去.所以只是在您的系统调用之后use autodie写入or
die $!,以便在出现问题时收到错误消息!
如果你use autodie qw/open flock
seek close/在顶部写的话,你的脚本是正确的.(实际上,您还应检查"print"是否有效或使用
File ::syswrite Slurp,或者
因为autodie无法检测到失败的print语句.)
无论如何,总结一下:
不要open $fh在$fh定义时.写,open my $fh以避免考虑这一点.
始终检查系统调用的返回值.让autodie为您完成此操作.
不要保持全球状态.不要编写一堆意味着要一起使用但依赖于隐式前提条件(如打开文件)的函数.如果函数具有前置条件,则将它们放在类中并使构造函数满足前提条件.这样,你就不会意外地写出错误的代码!
更新
好的,这是如何使这更多的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
}
Run Code Online (Sandbox Code Playgroud)
然后,我们可以在主程序中使用该类:
use LockedYAML;
my $resource = LockedYAML->new('filename');
print "Our object looks like: ". Dumper($resource->object);
$resource->write({ new => 'stuff' });
Run Code Online (Sandbox Code Playgroud)
错误将抛出异常,可以使用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;
}
Run Code Online (Sandbox Code Playgroud)
这类似地使用:
use LockedYAML;
my $resource = LockedYAML->new( file => 'filename' );
$resource->obj; # the object
$resource->obj( { new => 'object' }); # automatically saved to disk
Run Code Online (Sandbox Code Playgroud)
Moose版本更长,但运行时一致性检查更多,并且更容易增强.因人而异.
| 归档时间: |
|
| 查看次数: |
4766 次 |
| 最近记录: |