Domanda

Qual è il modo migliore per creare un blocco su un file in Perl?

È meglio affollare il file o creare un file di blocco su cui posizionare un blocco e verificare la presenza di un blocco sul file di blocco?

È stato utile?

Soluzione

Se finisci per usare il gregge, ecco del codice per farlo:

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' - $!";

Alcuni link utili:

In risposta alla tua domanda aggiuntiva, direi di posizionare il blocco sul file o di creare un file che chiami "blocco" ogni volta che il file è bloccato ed eliminarlo quando non è più bloccato (e quindi assicurati che i tuoi programmi rispettino quella semantica).

Altri suggerimenti

Le altre risposte coprono abbastanza bene il blocco del gregge Perl, ma su molti sistemi Unix/Linux ci sono in realtà due sistemi di blocco indipendenti:Blocchi basati su BSD Flock() e POSIX fcntl().

A meno che non forniate opzioni speciali da configurare durante la creazione di Perl, il suo stormo utilizzerà storm() se disponibile.Questo generalmente va bene e probabilmente è quello che desideri se hai solo bisogno di bloccare all'interno della tua applicazione (in esecuzione su un singolo sistema).Tuttavia, a volte è necessario interagire con un'altra applicazione che utilizza i blocchi fcntl() (come Sendmail, su molti sistemi) o forse è necessario eseguire il blocco dei file sui file system montati su NFS.

In questi casi, potresti voler dare un'occhiata File::FcntlLock O File::lockf.È anche possibile eseguire un lock basato su fcntl() in Perl puro (con alcune parti pelose e non portabili di pack()).

Panoramica rapida delle differenze stormo/fcntl/lockf:

lockf è quasi sempre implementato sopra fcntl, ha solo il blocco a livello di file.Se implementato utilizzando fcntl, le limitazioni riportate di seguito si applicano anche a lockf.

fcntl fornisce il blocco a livello di intervallo (all'interno di un file) e il blocco di rete su NFS, ma i blocchi non vengono ereditati dai processi figli dopo un fork().Su molti sistemi, è necessario che il filehandle sia aperto in sola lettura per richiedere un lock condiviso, e in lettura-scrittura per richiedere un lock esclusivo.

storm ha solo il blocco a livello di file, il blocco è solo all'interno di una singola macchina (puoi bloccare un file montato su NFS, ma solo i processi locali vedranno il blocco).I lock vengono ereditati dai figli (assumendo che il descrittore di file non sia chiuso).

A volte (sistemi SYSV) il gruppo viene emulato utilizzando lockf o fcntl;su alcuni sistemi BSD lockf viene emulato utilizzando storm.Generalmente questi tipi di emulazione funzionano male e si consiglia di evitarli.

CPAN in soccorso: IO::File bloccato.

Ryan P ha scritto:

In questo caso il file viene effettivamente sbloccato per un breve periodo di tempo mentre il file viene riaperto.

Quindi non farlo.Invece, open il file per lettura/scrittura:

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

Quando sei pronto per scrivere il contatore, basta seek torna all'inizio del file.Tieni presente che se lo fai, dovresti truncate appena prima close, in modo che il file non rimanga con spazzatura finale se i suoi nuovi contenuti sono più brevi di quelli precedenti.(Di solito, la posizione corrente nel file è alla fine, quindi puoi semplicemente scrivere truncate $fh, tell $fh.)

Inoltre, nota che ho usato tre argomenti open e un handle di file lessicale, e ho anche verificato la riuscita dell'operazione.Per favore evita gli handle di file globali (le variabili globali sono dannose, mmkay?) e i magici due argomenti open (che è stato la fonte di molti bug (n sfruttabili) nel codice Perl), e verifica sempre se il tuo openci riusciamo.

Penso che sarebbe molto meglio mostrarlo con le variabili lessicali come gestori di file e la gestione degli errori.È anche meglio usare le costanti del modulo Fcntl piuttosto che codificare il numero magico 2 che potrebbe non essere il numero giusto su tutti i sistemi operativi.

    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 $!";

Controlla l'intero documentazione del gregge e il Tutorial sul blocco dei file su PerlMonks anche se utilizza anche il vecchio stile di utilizzo della gestione dei file.

In realtà di solito salto la gestione degli errori su close() in quanto non c'è molto posso fare se fallisce comunque.

Per quanto riguarda cosa bloccare, se stai lavorando su un singolo file, blocca quel file.Se è necessario bloccare più file contemporaneamente, per evitare blocchi morti, è meglio scegliere un file da bloccare.Non importa se si tratta di uno dei tanti file che devi veramente bloccare o di un file separato creato solo a scopo di blocco.

Hai considerato l'utilizzo di LockFile::Modulo semplice?Fa già la maggior parte del lavoro per te.

Nella mia esperienza passata l'ho trovato molto facile da usare e robusto.

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' - $!";

Il mio obiettivo in questa domanda era bloccare un file utilizzato come archivio dati per diversi script.Alla fine ho usato un codice simile al seguente (da 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

Nel suo esempio ho rimosso lo stormo FILE, 8 poiché anche il comando close(FILE) esegue questa azione.Il vero problema era che all'avvio lo script deve mantenere il contatore corrente e quando termina deve aggiornare il contatore.È qui che Perl ha un problema, per leggere il file:

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

Ora voglio scrivere i risultati e poiché voglio sovrascrivere il file devo riaprirlo e troncarlo, il che risulta nel seguente:

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

In questo caso il file viene effettivamente sbloccato per un breve periodo di tempo mentre il file viene riaperto.Ciò dimostra il caso del file di blocco esterno.Se intendi modificare i contesti del file, utilizza un file di blocco.Il codice modificato:

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);

Sviluppato da 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: $!";
}

Un'alternativa alla serratura file L'approccio consiste nell'utilizzare un lucchetto PRESA.Vedere Blocco::Presa su CPAN per tale implementazione.L'utilizzo è semplice come il seguente:

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

Ci sono un paio di vantaggi nell'usare un socket:

  • garantito (tramite il sistema operativo) che non ci siano due applicazioni con lo stesso blocco:non esiste una condizione di gara.
  • garantito (sempre attraverso il sistema operativo) per una pulizia accurata all'uscita del processo, quindi non ci sono blocchi obsoleti da gestire.
  • si basa su funzionalità che sono ben supportate da tutto ciò su cui gira Perl:nessun problema con il supporto di storm(2) su Win32, ad esempio.

L'ovvio svantaggio è ovviamente che lo spazio dei nomi del lock è globale.È possibile una sorta di negazione del servizio se un altro processo decide di bloccare la porta di cui hai bisogno.

[divulgazione:Sono l'autore del suddetto modulo]

Usa il gregge Luca.

Modificare: Questo è una buona spiegazione.

storm crea blocchi di file in stile Unix ed è disponibile sulla maggior parte dei sistemi operativi su cui gira Perl.Tuttavia i blocchi dello stormo sono solo consultivi.

modificare:ha sottolineato che il gregge è trasportabile

Ecco la mia soluzione per leggere e scrivere in un unico lucchetto...

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$!";
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top