2012-02-20 9 views
13

Tuve un problema con la actualización de la lista de archivos después de eliminar un archivo. Cuando le pedí que eliminara el archivo, se lanzó la excepción porque el método de actualización intentó acceder a un archivo que se suponía que debía eliminarse.Esperando que el sistema elimine el archivo

Después de algunas reflexiones y depuración, llegué a la conclusión de que el problema era que el sistema necesita algún tiempo para eliminar un archivo. Y lo resuelvo así:

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

y funcionó bien.

Mi pregunta es

¿Existe una forma más elegante que esperar a que el sistema no eliminación de archivos y luego continuar con el código ...?

+1

Podemos ver el resto del código? Además, ¿qué tipo de sistema de archivos (NTFS local o alguna forma de NFS)? La mayoría de las operaciones de eliminación del sistema de archivos, en NTFS de todos modos, son atómicas. –

+0

Está en NTFS. ¿Qué parte del código le interesa? El método Eliminar elimina de forma recursiva todos los archivos en el directorio y el directorio en sí. No pensé que eso fuera relevante, así que dije que debía eliminar un archivo ... Es lo mismo, ¿no? – kr85

+1

No, en absoluto.Dejaré una respuesta –

Respuesta

5

La forma más elegante que puedo pensar es usando un FileSystemWatcher y suscríbase a su evento Deleted.

+3

Si esto está en una partición NFS, como sospecho que es, entonces FileSystemWatcher puede no ser confiable: http://stackoverflow.com/questions/239988/filesystemwatcher-vs-polling-to-watch-for-changes –

+1

@ChrisShain Solo he usado esto en un sistema NTFS y funciona muy bien. No estoy seguro acerca de NFS aunque – GETah

+0

Solución demasiado complicada supongo – Beatles1692

1

Eliminar un directorio usando Directory.Delete, específicamente the overload that takes a 'recursive' boolean, en NTFS, debe ser una operación atómica desde la perspectiva de su programa. No es necesario recurse manualmente.

+0

Debería ser, pero no lo es. Directory.Exists algunas veces devolverá true, especialmente cuando es la próxima línea. Peor aún, Directory.Create algunas veces lanzará cuando se le llame rápidamente después de Directory.Delete. – ILMTitan

0

Directory.Delete lanzará una excepción en el primer error que encuentre. Si desea continuar eliminando tantos archivos y subdirectorios como pueda, entonces no debería usar Directory.Delete y debería escribir su propia eliminación recursiva con try/catch blocks dentro de los bucles. Un ejemplo en el que puede querer hacerlo es si está tratando de limpiar los archivos temporales y uno de los archivos se ha bloqueado.

1

Aquí hay algunos códigos que usan FileWatcher. Lo que se quiere ser capaz de hacer es

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

el siguiente 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(); 
     } 
    } 
} 

y algunas utilidades que se aprovechan de las anteriores observable.

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

Esto funciona para mí:

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

me parece que la mayoría de las veces, no se introducirá el bucle while.

2

Código ligero para usar un FileSystemWatcher, suscríbase a su evento Deleted y espere.

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

siempre he utilizado este:

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

Ver here y here

Cuestiones relacionadas