你做錯了什麼是使用字符串「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