2012-02-20 1 views
13

У меня возникла проблема с обновлением списка файлов после удаления файла. Когда я дал команду удалить файл, исключение было выбрано потому, что метод refresh пытался получить доступ к файлу, который должен был быть удален.Ожидание системы для удаления файла

После некоторых мыслей и отладки я пришел к выводу, что проблема заключается в том, что системе требуется некоторое время для удаления файла. И я решаю это вот так:

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

, и он отлично работал.

Мой вопрос

Есть ли более элегантный способ ждать системы действительно удалить файл, а затем продолжить с кодом ...?

+1

Можем ли мы увидеть остальную часть кода? Кроме того, какая файловая система (локальная NTFS или какая-то форма NFS)? Большинство операций удаления файлов в NTFS в любом случае являются атомарными. –

+0

Это на NTFS. Какую часть кода вы интересуете. Метод удаления рекурсивно удаляет все файлы в каталоге и самом каталоге. Я не думал, что это важно, поэтому я сказал, что мне нужно удалить файл ... Это то же самое, не так ли? – kr85

+1

Совсем нет.Я оставлю ответ –

ответ

5

Самый элегантный способ, о котором я могу думать, это использовать FileSystemWatcher и подписаться на его Deleted событие.

+3

Если это в разделе NFS, как я подозреваю, тогда FileSystemWatcher может не быть надежным: http://stackoverflow.com/questions/239988/filesystemwatcher-vs-polling-to-watch-for-changes –

+1

@ChrisShain Я использовал это только в системе NTFS, и он отлично работает. Не уверен в NFS, хотя – GETah

+0

Слишком сложное решение, я полагаю – Beatles1692

1

Удаление каталога с использованием Directory.Delete, в частности the overload that takes a 'recursive' boolean, на NTFS, должно быть атомной операцией с точки зрения вашей программы. Не нужно вручную переписывать себя.

+0

Должно быть, но это не так. Directory.Exists иногда возвращают true, особенно когда это следующая строка. Хуже того, Directory.Create будет иногда бросать при вызове быстро после Directory.Delete. – ILMTitan

0

Directory.Delete выдает исключение при первой ошибке, с которой он сталкивается. Если вы хотите продолжить удаление как можно большего количества файлов и подкаталогов, вы не должны использовать Directory.Delete и должны написать собственное рекурсивное удаление с помощью блоков try/catch внутри циклов. Например, если вы захотите это сделать, это если вы пытаетесь очистить файлы temp и один из файлов был заблокирован.

1

Вот несколько примеров использования FileWatcher. То, что мы хотим, чтобы быть в состоянии сделать это

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

ниже реализует его

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

и некоторые утилиты, которые используют преимущества выше наблюдаемой.

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

Это работает для меня:

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

Я считаю, что большую часть времени, в то время как цикл не будет введен.

2

Легкий код для использования FileSystemWatcher, подпишитесь на его событие Deleted и подождите.

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

Я всегда использовал это:

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

См here и here