Pregunta

¿Cuál es la mejor manera de crear un bloqueo en un archivo en Perl?

¿Es mejor agruparse en el archivo o crear un archivo de bloqueo para colocar un bloqueo y verificar si hay un bloqueo en el archivo de bloqueo?

¿Fue útil?

Solución

Si terminas usando rebaño, aquí tienes un código para hacerlo:

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX) or die "Could not lock '$file' - $!";

# Do something with the file here...

# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock the
# file for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data may not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value of close() if you wrote to the file!
close($fh) or die "Could not write '$file' - $!";

Algunos enlaces útiles:

En respuesta a su pregunta adicional, diría que coloque el bloqueo en el archivo o cree un archivo que llame "bloquear" cada vez que el archivo esté bloqueado y lo elimine cuando ya no esté bloqueado (y luego asegúrese de que sus programas obedezcan esa semántica).

Otros consejos

Las otras respuestas cubren bastante bien el bloqueo de bandadas de Perl, pero en muchos sistemas Unix/Linux en realidad hay dos sistemas de bloqueo independientes:Bloqueos basados ​​en BSD Flock() y POSIX fcntl().

A menos que proporcione opciones especiales para configurar al compilar Perl, su rebaño usará rebaño() si está disponible.En general, esto está bien y probablemente sea lo que desee si solo necesita bloquear su aplicación (que se ejecuta en un solo sistema).Sin embargo, a veces necesita interactuar con otra aplicación que usa bloqueos fcntl() (como Sendmail, en muchos sistemas) o tal vez necesite bloquear archivos en sistemas de archivos montados en NFS.

En esos casos, es posible que desee consultar Archivo::FcntlLock o Archivo::lockf.También es posible realizar un bloqueo basado en fcntl() en Perl puro (con algunos fragmentos de pack() complicados y no portátiles).

Descripción rápida de las diferencias entre grey/fcntl/lockf:

lockf casi siempre se implementa sobre fcntl y solo tiene bloqueo a nivel de archivo.Si se implementa mediante fcntl, las limitaciones siguientes también se aplican a lockf.

fcntl proporciona bloqueo de nivel de rango (dentro de un archivo) y bloqueo de red a través de NFS, pero los procesos secundarios no heredan los bloqueos después de una bifurcación().En muchos sistemas, debe tener el identificador de archivo abierto en modo de solo lectura para solicitar un bloqueo compartido y en lectura y escritura para solicitar un bloqueo exclusivo.

Flock solo tiene bloqueo a nivel de archivo, el bloqueo es solo dentro de una sola máquina (puede bloquear un archivo montado en NFS, pero solo los procesos locales verán el bloqueo).Los niños heredan los bloqueos (suponiendo que el descriptor de archivo no esté cerrado).

A veces (sistemas SYSV) el rebaño se emula usando lockf o fcntl;en algunos sistemas BSD, lockf se emula utilizando Flock.Generalmente este tipo de emulación funciona mal y es mejor evitarlas.

CPAN al rescate: IO::ArchivoBloqueado.

Ryan P escribió:

En este caso, el archivo se desbloquea durante un breve período de tiempo mientras se vuelve a abrir.

Así que no hagas eso.En cambio, open el archivo para lectura/escritura:

open my $fh, '+<', 'test.dat'
    or die "Couldn’t open test.dat: $!\n";

Cuando esté listo para escribir el contador, simplemente seek volver al inicio del archivo.Tenga en cuenta que si hace eso, debería truncate justo antes close, para que el archivo no quede con basura si su nuevo contenido es más corto que el anterior.(Por lo general, la posición actual en el archivo está al final, por lo que puedes simplemente escribir truncate $fh, tell $fh.)

Además, tenga en cuenta que utilicé tres argumentos. open y un identificador de archivo léxico, y también verifiqué el éxito de la operación.Evite los identificadores de archivos globales (las variables globales son malas, ¿vale?) y los dos argumentos mágicos open (que ha sido una fuente de muchos errores explotables en el código Perl), y siempre pruebe si su openTenemos éxito.

Creo que sería mucho mejor mostrar esto con variables léxicas como manejadores de archivos y manejo de errores.También es mejor usar las constantes del módulo Fcntl que codificar el número mágico 2, que podría no ser el número correcto en todos los sistemas operativos.

    use Fcntl ':flock'; # import LOCK_* constants

    # open the file for appending
    open (my $fh, '>>', 'test.dat') or die $!;

    # try to lock the file exclusively, will wait till you get the lock
    flock($fh, LOCK_EX);

    # do something with the file here (print to it in our case)

    # actually you should not unlock the file
    # close the file will unlock it
    close($fh) or warn "Could not close file $!";

Mira el completo documentación del rebaño y el Tutorial de bloqueo de archivos en PerlMonks a pesar de que también utiliza el estilo antiguo de uso del identificador de archivos.

En realidad, generalmente omito el manejo de errores en Close (), ya que no hay mucho que pueda hacer si falla de todos modos.

Con respecto a qué bloquear, si está trabajando en un solo archivo, bloquee ese archivo.Si necesita bloquear varios archivos a la vez, para evitar bloqueos muertos, es mejor elegir un archivo que esté bloqueando.Realmente no importa si ese es uno de los varios archivos que realmente necesita bloquear o un archivo separado que crea solo para bloquearlo.

¿Ha considerado utilizar el LockFile::Módulo simple?Ya hace la mayor parte del trabajo por usted.

En mi experiencia pasada, lo encontré muy fácil de usar y resistente.

use strict;

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is in quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX);


# Do something with the file here...


# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock it
# for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data will not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value on close()!
close($fh) or die "Could not write '$file' - $!";

Mi objetivo en esta pregunta era bloquear un archivo que se utiliza como almacén de datos para varios scripts.Al final utilicé un código similar al siguiente (de Chris):

open (FILE, '>>', test.dat') ; # open the file 
flock FILE, 2; # try to lock the file 
# do something with the file here 
close(FILE); # close the file

En su ejemplo, eliminé el ARCHIVO rebaño, 8 ya que cerrar (ARCHIVO) también realiza esta acción.El verdadero problema era que cuando se inicia el script tiene que mantener el contador actual y cuando finaliza tiene que actualizar el contador.Aquí es donde Perl tiene un problema, para leer el archivo:

 open (FILE, '<', test.dat');
 flock FILE, 2;

Ahora quiero escribir los resultados y como quiero sobrescribir el archivo necesito volver a abrirlo y truncarlo, lo que da como resultado lo siguiente:

 open (FILE, '>', test.dat'); #single arrow truncates double appends
 flock FILE, 2;

En este caso, el archivo se desbloquea durante un breve período de tiempo mientras se vuelve a abrir.Esto demuestra el caso del archivo de bloqueo externo.Si va a cambiar los contextos del archivo, utilice un archivo de bloqueo.El código modificado:

open (LOCK_FILE, '<', test.dat.lock') or die "Could not obtain lock";
flock LOCK_FILE, 2;
open (FILE, '<', test.dat') or die "Could not open file";
# read file
# ...
open (FILE, '>', test.dat') or die "Could not reopen file";
#write file
close (FILE);
close (LOCK_FILE);

Desarrollado a partir de http://metacpan.org/pod/File::FcntlLock

use Fcntl qw(:DEFAULT :flock :seek :Fcompat);
use File::FcntlLock;
sub acquire_lock {
  my $fn = shift;
  my $justPrint = shift || 0;
  confess "Too many args" if defined shift;
  confess "Not enough args" if !defined $justPrint;

  my $rv = TRUE;
  my $fh;
  sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!";
  $fh->autoflush(1);
  ALWAYS "acquiring lock: $fn";
  my $fs = new File::FcntlLock;
  $fs->l_type( F_WRLCK );
  $fs->l_whence( SEEK_SET );
  $fs->l_start( 0 );
  $fs->lock( $fh, F_SETLKW ) or LOGDIE  "failed to get write lock: $fn:" . $fs->error;
  my $num = <$fh> || 0;
  return ($fh, $num);
}

sub release_lock {
  my $fn = shift;
  my $fh = shift;
  my $num = shift;
  my $justPrint = shift || 0;

  seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!";
  print $fh "$num\n" or LOGDIE "write failed: $fn: $!";
  truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!";
  my $fs = new File::FcntlLock;
  $fs->l_type(F_UNLCK);
  ALWAYS "releasing lock: $fn";
  $fs->lock( $fh, F_SETLK ) or LOGDIE "unlock failed: $fn: " . $fs->error;
  close($fh) or LOGDIE "close failed: $fn: $!";
}

Una alternativa a la cerradura archivo El enfoque es usar un candado. enchufe.Ver Cerradura::Zócalo en CPAN para tal implementación.El uso es tan simple como lo siguiente:

use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken

Hay un par de ventajas al usar un enchufe:

  • garantizado (a través del sistema operativo) que no habrá dos aplicaciones que mantengan el mismo bloqueo:no hay condición de carrera.
  • Se garantiza (nuevamente a través del sistema operativo) que se limpiará perfectamente cuando finalice el proceso, por lo que no hay bloqueos obsoletos con los que lidiar.
  • se basa en una funcionalidad que es compatible con cualquier cosa en la que se ejecute Perl:No hay problemas con el soporte de Flock(2) en Win32, por ejemplo.

La desventaja obvia es, por supuesto, que el espacio de nombres de bloqueo es global.Es posible que se produzca una especie de denegación de servicio si otro proceso decide bloquear el puerto que necesita.

[divulgación:Soy el autor del módulo mencionado anteriormente]

Utilizar el rebaño Lucas.

Editar: Este es una buena explicación.

Flock crea bloqueos de archivos estilo Unix y está disponible en la mayoría de los sistemas operativos en los que se ejecuta Perl.Sin embargo, los candados de rebaño son sólo de asesoramiento.

editar:destacó que el rebaño es portátil

Aquí está mi solución para leer y escribir en un solo candado...

open (TST,"+< readwrite_test.txt") or die "Cannot open file\n$!";
flock(TST, LOCK_EX);
# Read the file:
@LINES=<TST>;
# Wipe the file:
seek(TST, 0, 0); truncate(TST, 0);
# Do something with the contents here:
push @LINES,"grappig, he!\n";
$LINES[3]="Gekke henkie!\n";
# Write the file:
foreach $l (@LINES)
{
   print TST $l;
}
close(TST) or die "Cannot close file\n$!";
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top