2012-02-20 6 views
13

Ho avuto un problema con l'aggiornamento dell'elenco file dopo l'eliminazione di un file. Quando ho dato il comando di eliminare il file, l'eccezione è stata generata perché il metodo di aggiornamento ha tentato di accedere a un file che avrebbe dovuto essere cancellato.In attesa del sistema per eliminare il file

Dopo un po 'di riflessione e debug sono giunto alla conclusione che il problema era che il sistema ha bisogno di tempo per cancellare un file. E ho risolto in questo modo:

//Deleting file 
System.Threading.Thread.Sleep(2000); 
//Refreshing list 

e ha funzionato bene.

La mia domanda è

C'è un modo più elegante per attendere che il sistema non cancellare file e poi continuare con il codice ...?

+1

Possiamo vedere il resto del codice? Inoltre, che tipo di filesystem (NTFS locale o qualche forma di NFS)? La maggior parte delle operazioni di cancellazione del filesystem, comunque su NTFS, sono atomiche. –

+0

È su NTFS. Quale parte del codice ti interessa. Il metodo Delete elimina in modo ricorsivo tutti i file nella directory e nella directory stessa. Non pensavo che fosse rilevante quindi ho detto che ho bisogno di cancellare un file ... È la stessa cosa, non è vero? – kr85

+1

Niente affatto.Lascerò una risposta –

risposta

5

Il modo più elegante che riesco a pensare è usare uno FileSystemWatcher e iscriversi al suo evento Deleted.

+3

Se questo è su una partizione NFS, come sospetto che sia, quindi FileSystemWatcher potrebbe non essere affidabile: http://stackoverflow.com/questions/239988/filesystemwatcher-vs-polling-to-watch-for-changes –

+1

@ChrisShain L'ho usato solo su un sistema NTFS e funziona alla grande. Non sono sicuro di NFS anche se – GETah

+0

Soluzione troppo complicata presumo – Beatles1692

1

L'eliminazione di una directory utilizzando Directory.Delete, in particolare the overload that takes a 'recursive' boolean, su NTFS, dovrebbe essere un'operazione atomica dal punto di vista del programma. Non è necessario recidare manualmente da soli.

+0

Dovrebbe essere, ma non lo è. Directory.Exists a volte restituisce true, soprattutto quando è la riga successiva. Peggio ancora, Directory.Create a volte viene lanciata quando viene chiamata rapidamente dopo Directory.Delete. – ILMTitan

0

Directory.Delete genererà un'eccezione sul primo errore rilevato. Se si desidera continuare a eliminare il maggior numero di file e sottodirectory possibile, non utilizzare Directory.Delete e scrivere la propria eliminazione ricorsiva con i blocchi try/catch nei loop. Un esempio in cui si potrebbe voler fare è se si sta tentando di pulire i file temporanei e uno dei file è stato bloccato.

1

Ecco un codice che utilizza FileWatcher. Quello che noi vogliamo essere in grado di fare è

await Utils.DeleteDirectoryAsync("c:\temp\foo", recurse: true); 

sottostante implementa

using System; 
using System.IO; 
using System.Reactive; 
using System.Reactive.Linq; 
using System.Reactive.Subjects; 
using System.Threading.Tasks; 

namespace Utils 
{ 
    internal class FileWatcher : IDisposable 
    { 
     readonly FileSystemWatcher _Watcher; 

     public Subject<FileSystemEventArgs> Changed = new Subject<FileSystemEventArgs>(); 

     public FileWatcher(string file) 
     { 
      // Create a new FileSystemWatcher and set its properties. 
      _Watcher = new FileSystemWatcher 
         { 
          Path = Path.GetDirectoryName(file), 
          NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite 
              | NotifyFilters.FileName | NotifyFilters.DirectoryName, 
          Filter =Path.GetFileName(file) 
         }; 

      // Add event handlers. 
      _Watcher.Changed += OnChanged; 
      _Watcher.Created += OnChanged; 
      _Watcher.Deleted += OnChanged; 
      _Watcher.Renamed += OnChanged; 

      // Begin watching. 
      _Watcher.EnableRaisingEvents = true; 
     } 

     // Define the event handlers. 
     private void OnChanged(object source, FileSystemEventArgs e) 
     { 
      Changed.OnNext(e); 
     } 


     public void Dispose() 
     { 
      _Watcher.Dispose(); 
     } 
    } 
} 

e alcuni utils che sfruttano quanto sopra osservabile.

public static class FileUtils 
{ 
    public static IObservable<FileSystemEventArgs> ChangedObservable(string path) 
    { 
     if (path == null) 
      return Observable.Never<FileSystemEventArgs>(); 

     return Observable.Using(() => new FileWatcher(path), watcher => watcher.Changed); 
    } 

    public static Task DeleteDirectoryAsync(string path, bool recurse) 
    { 
     var task = new TaskCompletionSource<Unit>(); 

     if (Directory.Exists(path)) 
     { 
      ChangedObservable(path) 
       .Where(f => f.ChangeType == WatcherChangeTypes.Deleted) 
       .Take(1) 
       .Subscribe(v => task.SetResult(Unit.Default)); 

      Directory.Delete(path, recurse); 
     } 
     else 
     { 
      task.SetResult(Unit.Default); 
     } 

     return task.Task; 
    } 
} 
9

Questo funziona per me:

public static void DeleteFile(String fileToDelete) 
{ 
    var fi = new System.IO.FileInfo(fileToDelete); 
    if (fi.Exists) 
    { 
     fi.Delete(); 
     fi.Refresh(); 
     while (fi.Exists) 
     { System.Threading.Thread.Sleep(100); 
      fi.Refresh(); 
     } 
    } 
} 

trovo che la maggior parte del tempo, non sarà iscritto il ciclo while.

2

Codice leggero per utilizzare un FileSystemWatcher, sottoscrivere l'evento Deleted e attendere.

void DeleteFileAndWait(string filepath, int timeout = 30000) 
{ 
    using (var fw = new FileSystemWatcher(Path.GetDirectoryName(filepath), Path.GetFileName(filepath))) 
    using (var mre = new ManualResetEventSlim()) 
    { 
     fw.EnableRaisingEvents = true; 
     fw.Deleted += (object sender, FileSystemEventArgs e) => 
     { 
      mre.Set(); 
     }; 
     File.Delete(filepath); 
     mre.Wait(timeout); 
    } 
} 
0

ho sempre usato questo:

System.GC.Collect(); 
System.GC.WaitForPendingFinalizers(); 

Vedi here e here