2010-08-10 18 views
5

Sto cercando una soluzione già implementata per la creazione atomica di un blocco di file in MATLAB.creazione di un blocco di file in MATLAB (file mutex)

Qualcosa di simile:

file_lock('create', 'mylockfile'); %this will block until it creates the lock file. 
file_lock('remove', 'mylockfile'); %this will remove the lock file: 

Questa domanda è già stato chiesto più volte, con alcune idee proposte di soluzione (come l'utilizzo di Java FileLock), ma non ho trovato una soluzione semplice già implementato.

Sei a conoscenza di una soluzione così implementata?

Note:

+3

Odio essere una coperta bagnata, ma questo è estremamente difficile da ottenere corretto in un modo generale, specialmente per i file di rete. Il blocco dei file dipende molto dal sistema. Non ci sarà una soluzione semplice, già implementata, che non sia rotta. (Non è difficile scrivere qualcosa che "sembra funzionare per lo più"; difficile scrivere qualcosa che non mancherà in produzione da qualche parte.) Facciamo un passo indietro: cosa stai cercando di sincronizzare l'accesso? È il contenuto del file o i file rappresentano un'altra risorsa? Quali piattaforme stai prendendo di mira? In che modo "corretto" hai bisogno dell'esclusione? –

risposta

0

Scrivi un nuovo file, quindi rinominarlo. Rinominare è un'operazione atomica e tutti i nuovi contenuti diventeranno visibili contemporaneamente.

+0

Questa idea è stata proposta anche in http://groups.google.com/group/comp.soft-sys.matlab/browse_thread/thread/eb4ebeb8700ea440/13bd6710e8429a70?lnk=raot ma si scopre che la rinomina di unix non è atomica (per prima cosa cancella il file dest, quindi il file sorgente viene rinominato) e matlab usa il nome di unix in unix, quindi questa soluzione non funziona nemmeno. –

+0

Basta leggere quel thread. Il consenso sembra essere che il comando 'movefile' di MatLab fa più di una semplice chiamata' rename', quindi 'movefile' non è atomico. Il SUS è abbastanza chiaro che 'rename' è atomico su unix (ovviamente, se stai usando un clone simile a Unix che non rispetta le specifiche che sono una storia diversa). http://opengroup.org/onlinepubs/007908775/xsh/rename.html –

0

Alla fine ho eseguito un'implementazione basata su due test consecutivi (movefile e verificare il contenuto del file spostato).

non molto ben scritto, ma funziona per ora per me.

+++++ file_lock.m ++++++++++++++++++++++++

function file_lock(op, filename) 
%this will block until it creates the lock file: 
%file_lock('create', 'mylockfile') 
% 
%this will remove the lock file: 
%file_lock('remove', 'mylockfile') 


% todo: verify that there are no bugs 

filename = [filename '.mat']; 

if isequal(op, 'create') 
    id = [tempname() '.mat'] 
    while true 
    save(id, 'id'); 
    success = fileattrib(id, '-w'); 
    if success == 0; error('fileattrib'); end 

    while true 
     if exist(filename, 'file');  %first test 
     fprintf('file lock exists(1). waiting...\n'); 
     pause(1); 
     continue; 
     end 
     status = movefile(id, filename); %second test 
     if status == 1; break; end 
     fprintf('file lock exists(2). waiting...\n'); 
     pause(1); 
    end 

    temp = load(filename, 'id');   % third test. 
    if isequal(id, temp.id); break; end 

    fprintf('file lock exists(3). waiting...\n'); 
    pause(1) 
    end 

elseif isequal(op, 'remove') 
    %delete(filename); 
    execute_rs(@() delete(filename)); 

else 
    error('invalid op'); 
end 



function execute_rs(f) 
while true 
    try 
    lastwarn(''); 
    f(); 
    if ~isequal(lastwarn, ''); error(lastwarn); end %such as: Warning: File not found or permission denied 
    break; 
    catch exception 
    fprintf('Error: %s\n.Retrying...\n', exception.message); 
    pause(.5); 
    end 
end 

+++++++ ++++++++++++++++++++

+0

Penso che tu abbia un problema se un lavoratore viene interrotto subito prima di 'movefile'. –

5

Ho optato per una soluzione piuttosto semplice per combinare errore/registrazione dei messaggi da più thread di lavoro in un singolo file. Ogni volta che voglio scrivere su quel file, prima scrivo l'output sul file temporaneo del thread stesso. Successivamente, aggiungo il file temporaneo al file di log "principale" utilizzando flock. Saltando alcuni dettagli qui, l'idea è:

fid=fopen(threadtemp, 'w'); 
fprintf(fid, 'Error message goes here'); 
fclose(fid); 

runme = sprintf('flock -x %s -c ''cat %s >> %s''', LOGFILE, threadtemp, LOGFILE); 
system(runme); 

vedere la pagina man gregge per i dettagli, ma la chiamata di cui sopra è l'acquisizione di un blocco esclusivo sul file di log, che esegue il previsto comando sotto la serratura, e poi rilasciandolo .

Questo ovviamente funziona solo se si è su un sistema che ha flock (Linux/OS X, e solo alcuni tipi di file system) e si sta facendo qualcosa che può essere fatto dalla riga di comando, ma Scommetto che è un caso d'uso piuttosto comune.

+0

solo chiedendo, perché si passa attraverso la procedura di creazione del file temporaneo? perché non solo qualcosa come il sistema ('flock -x logfile -c' 'echo errormessage >> logfile' '')? grazie – Grittathh

+0

Nessun motivo particolare - il tuo approccio dovrebbe funzionare bene. Penso che alcune delle mie funzioni originali abbiano preso i nomi dei file per l'accesso, quindi questo mi permette di usarle senza modifiche, ma è per lo più rudimentale, immagino. L'unico vero trucco è usare system() per chiamare flock. –

1

A seconda della versione di Java che si sta utilizzando, forse questo sarà il lavoro (tradotto da: http://www.javabeat.net/2007/10/locking-files-using-java/)

classdef FileLock < handle 
    properties (Access = private) 
     fileLock = [] 
     file 
    end 

    methods 
     function this = FileLock(filename) 
      this.file = java.io.RandomAccessFile(filename,'rw'); 
      fileChannel = this.file.getChannel(); 
      this.fileLock = fileChannel.tryLock(); 
     end 

     function val = hasLock(this) 
      if ~isempty(this.fileLock) && this.fileLock.isValid() 
       val = true; 
      else 
       val = false; 
      end 
     end 

     function delete(this) 
      this.release(); 
     end 

     function release(this) 
      if this.hasLock 
       this.fileLock.release(); 
      end 
      this.file.close 
     end 
    end 
end 

Uso sarebbe:

lock = FileLock('my_lock_file'); 
if lock.hasLock 
    %// do something here 
else 
    %// I guess not 
end 
%// Manually release the lock, or just delete (or let matlab clean it up) 

Mi piace il modello obj involucro per IO in modo che il rilascio avvenga anche in eccezioni

MODIFICA: il file ref deve essere tenuto in giro e chiuso manualmente o non sarà possibile modificarlo. Ciò significa che questo codice è veramente utile solo per i file di blocco puro, penso.

0

Se avete solo bisogno di girare su OS X e Linux (non Windows), è possibile utilizzare il seguente:

pathLock='/tmp/test.lock' 

% Try to create and lock this file. 
% In my case I use -r 0 to avoid retrying 
% You could use -r -1 to retry forever, or for a particular amount of time, 
% etc, see `man lockfile` for details. 
if ~system(sprintf('lockfile -r 0 %s',pathLock)) 
    % We succeeded, so perform some task which needs to be serialized. 
    % runSerializedTask() 
    % Now remove the lockfile 
    system(sprintf('rm -f %s',pathLock)); 
end 
+0

Si noti che c'è molta discussione sul fatto che approcci come questo siano sicuri su file system in rete. La pagina man lockfile dice che è "resistente NFS", ma non sono sicuro di cosa significhi :) o se copre tutte le preoccupazioni su questo (diventano piuttosto complicate!) – nonagon