То, что вы делаете неправильно, использует строку "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
. Готово. Теперь вы можете
использовать этот код (и позволить другим делать то же самое), не беспокоясь о
что-то не так. Меньше стресса.
Другая вещь, которую мы здесь делаем, это проверка ошибок. Да, диски могут
потерпеть поражение. Файлы могут быть опечатаны. Если вы блаженно игнорируете возвращаемое значение
из открытых и стадных, то ваша программа может не делать то, что вы думаете
это делает. Файл не может быть открыт. Файл может не быть
заблокирован правильно. Однажды ваша программа не будет работать должным образом
потому что вы записали «file» как «flie» и файл не может быть открыт.
Вы будете часами чесать голову, задаваясь вопросом, что происходит.
В конце концов, вы сдадитесь, пойдете домой и повторите попытку позже. Этот раз,
Вы не будете опечатывать имя файла, и оно будет работать. Несколько часов будет
были потрачены впустую. Ты умрешь на несколько лет раньше, чем должна
из-за накопленного стресса. Так что просто use autodie
или напишите or
die $!
после системных вызовов, чтобы вы получили сообщение об ошибке, когда
что-то идет не так!
Ваш сценарий будет правильным, если вы напишите use autodie qw/open flock
seek close/
вверху. (На самом деле, вы также должны проверить, что
"печать" работал или использовать
Файл :: Slurp или
syswrite
, поскольку autodie не может обнаружить ошибочный оператор print
.
Так или иначе, чтобы подвести итог:
Не open $fh
, если определено $fh
. Напишите open my $fh
в
не думай об этом.
Всегда проверяйте возвращаемые значения системных вызовов. Заставь автодие сделать
это для вас.
Не сохранять глобальное состояние. Не пишите кучу функций, которые
предназначены для совместного использования, но полагаются на неявные предварительные условия
как открытый файл. Если у функций есть предварительные условия, поместите их в
класс и заставить конструктор удовлетворить предварительные условия.
Таким образом, вы не можете случайно написать код ошибки!
Обновление
Хорошо, вот как это сделать больше ОО. Сначала мы сделаем "чистый Perl" OO
и затем используйте Moose . Лось
что бы я использовал для любой реальной работы; «чистый Perl» только для
ради облегчения понимания для кого-то нового для ОО и
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' });
Ошибки будут вызывать исключения, которые могут быть обработаны с помощью
Попробуйте :: Tiny и YAML
файл останется заблокированным, пока экземпляр существует. Вы можете, из
Конечно, у нас есть много объектов LockedYAML одновременно, поэтому мы
сделал это ОО.
И, наконец, версия Moose:
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
Версия Moose длиннее, но обеспечивает гораздо большую согласованность во время выполнения
проверка и его легче улучшить. YMMV.