Вы можете использовать сложный протокол сотрудничества, такой как в следующем. Оба конца, reader
и writer
, используют общий код в модуле TakeTurns
, который обрабатывает суетливые детали, такие как блокировка и место, где находится файл блокировки. Клиентам нужно только указать, что они хотят делать, когда у них есть эксклюзивный доступ к файлу.
Читатель
#! /usr/bin/perl
use warnings;
use strict;
use TakeTurns;
my $runs = 0;
reader "file.txt" =>
sub {
my($fh) = @_;
my @lines = <$fh>;
print map "got: $_", @lines;
++$runs <= 10;
};
автор
#! /usr/bin/perl
use warnings;
use strict;
use TakeTurns;
my $n = 10;
my @chars = ('a'..'z','A'..'Z','0'..'9','_');
writer "file.txt" =>
sub { my($fh) = @_;
print $fh join("" => map $chars[rand @chars], 1..$n), "\n"
or warn "$0: print: $!";
};
Модуль TakeTurns
выполняет на работе:
package TakeTurns;
use warnings;
use strict;
use Exporter 'import';
use Fcntl qw/ :DEFAULT :flock /;
our @EXPORT = qw/ reader writer /;
my $LOCKFILE = "/tmp/taketurns.lock";
sub _loop ($&) {
my($path,$action) = @_;
while (1) {
sysopen my $lock, $LOCKFILE, O_RDWR|O_CREAT
or die "sysopen: $!";
flock $lock, LOCK_EX or die "flock: $!";
my $continue = $action->();
close $lock or die "close: $!";
return unless $continue;
sleep 0;
}
}
sub writer {
my($path,$w) = @_;
_loop $path =>
sub {
open my $fh, ">", $path or die "open $path: $!";
my $continue = $w->($fh);
close $fh or die "close $path: $!";
$continue;
};
}
sub reader {
my($path,$r) = @_;
_loop $path =>
sub {
open my $fh, "<", $path or die "open $path: $!";
my $continue = $r->($fh);
close $fh or die "close $path: $!";
$continue;
};
}
1;
Пример вывода:
got: 1Upem0iSfY
got: qAALqegWS5
got: 88RayL3XZw
got: NRB7POLdu6
got: IfqC8XeWN6
got: mgeA6sNEpY
got: 2TeiF5sDqy
got: S2ksYEkXsJ
got: zToPYkGPJ5
got: 6VXu6ut1Tq
got: ex0wYvp9Y8
Даже несмотря на то, что у вас было так много проблем, все еще есть проблемы. Протокол ненадежен, поэтому reader
не может гарантировать просмотр всех сообщений, отправляемых writer
. Если writer
не активен, reader
может читать одно и то же сообщение снова и снова.
Вы можете добавить все это, но более разумным подходом будет использование абстракций, которые уже предоставляет операционная система.
Например, Unix именованные каналы кажутся довольно близкими к тому, что вы хотите, и обратите внимание, насколько простой код:
pread
#! /usr/bin/perl
use warnings;
use strict;
my $pipe = "/tmp/mypipe";
system "mknod $pipe p 2>/dev/null";
open my $fh, "<", $pipe or die "$0: open $pipe: $!";
while (<$fh>) {
print "got: $_";
sleep 0;
}
pwrite
#! /usr/bin/perl
use warnings;
use strict;
my $pipe = "/tmp/mypipe";
system "mknod $pipe p 2>/dev/null";
open my $fh, ">", $pipe or die "$0: open $pipe: $!";
my $n = 10;
my @chars = ('a'..'z','A'..'Z','0'..'9','_');
while (1) {
print $fh join("" => map $chars[rand @chars], 1..$n), "\n"
or warn "$0: print: $!";
}
Оба конца пытаются создать канал, используя mknod
, потому что у них нет другого метода синхронизации. По крайней мере, один из них выйдет из строя, но нам все равно, пока существует труба.
Как видите, все механизмы ожидания обрабатываются системой, поэтому вы делаете то, что вам нужно: чтение и запись сообщений.