Domanda

Ho un'applicazione che registra le informazioni in un file di testo al giorno ogni secondo su un PC master. Un PC slave sulla rete utilizzando la stessa applicazione vorrebbe copiare questo file di testo alla sua unità locale. Posso vedere ci sta per essere problemi di accesso ai file.

Questi file non deve essere più grande di 30-40MB ciascuno. la rete sarà 100MB ethernet. Posso vedere esiste un potenziale per il processo di copia di prendere più di 1 secondo significa che il PC di registrazione sarà necessario aprire il file per la scrittura mentre è in corso la lettura.

Qual è il metodo migliore per la scrittura del file (logging) e la copia di file di procedure? So che c'è la procedura standard di Windows CopyFile (), tuttavia questo mi ha dato problemi di accesso ai file. C'è anche TFileStream utilizzando il flag fmShareDenyNone, ma questo anche molto di tanto in tanto mi dà un problema di accesso troppo (come 1 a settimana).

Che cosa è questo il modo migliore per realizzare questo compito?

Il mio attuale Registrazione del file:

procedure FSWriteline(Filename,Header,s : String);
var LogFile : TFileStream;
line : String;
begin
     if not FileExists(filename) then
     begin
          LogFile := TFileStream.Create(FileName, fmCreate or fmShareDenyNone);
          try
             LogFile.Seek(0,soFromEnd);
             line := Header + #13#10;
             LogFile.Write(line[1],Length(line));
             line := s + #13#10;
             LogFile.Write(line[1],Length(line));
          finally
                 logfile.Free;
          end;
     end else begin
         line := s + #13#10;
         Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
         try
            logfile.Seek(0,soFromEnd);
            Logfile.Write(line[1], length(line));
         finally
            Logfile.free;
         end;
     end;
end;

La mia procedura di copia dei file:

procedure DoCopy(infile, Outfile : String);
begin
     ForceDirectories(ExtractFilePath(outfile)); //ensure folder exists
     if FileAge(inFile) = FileAge(OutFile) then Exit; //they are the same modified time
     try
        { Open existing destination }
        fo := TFileStream.Create(Outfile, fmOpenReadWrite or fmShareDenyNone);
        fo.Position := 0;
     except
           { otherwise Create destination }
           fo := TFileStream.Create(OutFile, fmCreate or fmShareDenyNone);
     end;
     try
        { open source }
        fi := TFileStream.Create(InFile, fmOpenRead or fmShareDenyNone);
        try
           cnt:= 0;
           fi.Position := cnt;
           max := fi.Size;
           {start copying }
           Repeat
                 dod := BLOCKSIZE; // Block size
                 if cnt+dod>max then dod := max-cnt;
                 if dod>0 then did := fo.CopyFrom(fi, dod);
                 cnt:=cnt+did;
                 Percent := Round(Cnt/Max*100);
           until (dod=0)
        finally
               fi.free;
        end;
     finally
            fo.free;
     end;
end;
È stato utile?

Soluzione

vorrei suggerire di non chiudere e riaprire il file condiviso più e più volte per cominciare. Dal momento che si scrive ad esso ogni secondo, che è solo in testa inutile.

Sul lato master, creare e chiudere il file (il flag fmCreate non può essere utilizzato con altre bandiere!), Poi ri-aprirlo in modalità fmOpenWrite con la condivisione fmShareDenyWrite, lasciarla aperta, e scrivere quando necessario.

Sul lato slave, aprire il file in modalità fmOpenRead con la condivisione fmShareDenyNone, lasciarlo aperto, e leggere da esso ogni secondo. Non c'è bisogno di copiare l'intero file condiviso in rete ogni volta. Questo è larghezza di banda sprecata. Basta leggere ciò che i nuovi dati è stato scritto negli ultimi secondi e questo è tutto. Se lo slave ha bisogno dei dati da memorizzare in un file locale, allora si può gestire un file locale separata in modo indipendente del file condiviso, spingendo i nuovi dati nel file locale quando necessario.

Altri suggerimenti

Per gestire la sua specifica occasionale problema ricorrente:

non dire quale versione di Delphi che si sta utilizzando.

C'è un bug nel costruttore TFileStream.Create () fino al versione 2007 (almeno). Questo potrebbe spiegare i vostri problemi di concorrenza occasionali.

Detto questo, credo che il bug è più suscettibile di provocare i file non vengono creati come previsto (quando uno ShareMode è inoltre specificato), tho questo può allora a loro volta portano al tuo problema di concorrenza.

Un modo per aggirare questo potrebbe essere quando il file deve essere creato, creare innanzitutto il file poi semplicemente aprirlo per la scrittura come una chiamata del costruttore separato - questo in realtà rende la creazione di file una fase separata, con file di scrittura di una parte consistente della processo:

  if not FileExists(filename) then
  begin
    // There may be a more efficient way of creating an empty file, but this 
    //  illustrates the approach

    LogFile := TFileStream.Create(FileName, fmCreate);
    LogFile.Free;

    line := Header + #13#10 + s + #13#10;
  end
  else
    line := s + #13#10;

  Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
  try
    logfile.Seek(0,soFromEnd);
    Logfile.Write(line[1], length(line));
  finally
    Logfile.free;
  end;

Utilizza lo standard Append creazione del file / comando di apertura, l'uso write per aggiornare il registro, e close immediatamente il file.

Utilizzare un lavoro sul sistema operativo per copiare / spostare i file; lo hanno riprovare e lanciare ad una maggiore frequenza di quello che si richiede.

Se si vuole fare dal di dentro Delphi quindi utilizzare MoveFile per spostare il tutto.

Si potrebbe desiderare di avvolgere entrambe le scritture di registro e le mosse in try-except modo che possano essere processati nuovamente un numero ragionevole di volte se il file system (NTFS su Windows?) Non risolve il concorrenza per voi. Nel peggiore dei casi, sia:

  1. Il file ottenuto spostato e esso venga nuovamente e scritto.
  2. Il file non viene spostato subito perché è in fase di scrittura.

Se il sistema operativo non risolve la condizione di competizione, allora si dovrà dare priorità all'azione Starved utilizzando un semaforo / blocco.

la ricerca di una funzione chiamata "IsFileInUse" o qualcosa di simile, sono sicuro che è possibile utilizzare che, come:

// master
while IsFileInUse(*AFileName*) do
  Sleep(10);
write-content-to-file

// slave
while IsFileInUse(*AFileName*) do
  Sleep(10);
copy-file-to-a-special-location

e presto !! il gioco è fatto !!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top