Ожидание удаления файла системой

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

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

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

и все работало нормально.

Мой вопрос

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


person kr85    schedule 20.02.2012    source источник
comment
Можем ли мы увидеть остальной код? Кроме того, какая файловая система (локальная NTFS или какая-либо форма NFS)? Большинство операций удаления файловой системы, в любом случае в NTFS, являются атомарными.   -  person Chris Shain    schedule 21.02.2012
comment
Это на NTFS. Какая часть кода вас интересует. Метод Delete рекурсивно удаляет все файлы в каталоге и сам каталог. Я не думал, что это актуально, поэтому сказал, что мне нужно удалить файл ... Это то же самое, не так ли?   -  person kr85    schedule 21.02.2012
comment
Нисколько. Я оставлю ответ   -  person Chris Shain    schedule 21.02.2012
comment
@ kr85 Пожалуйста, посмотрите мой пост, я только что обновил ссылку MSDN FileSystemWatcher, чтобы указать на .NET 4.0 вместо 1.1   -  person GETah    schedule 21.02.2012


Ответы (7)


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

person GETah    schedule 20.02.2012
comment
Если это находится в разделе NFS, как я подозреваю, тогда FileSystemWatcher может быть ненадежным: stackoverflow.com/questions/239988/ - person Chris Shain; 21.02.2012
comment
@ChrisShain Я использовал это только в системе NTFS, и он отлично работает. Не уверен насчет NFS - person GETah; 21.02.2012
comment
Я полагаю, слишком сложное решение - person Beatles1692; 21.02.2012
comment
@ Beatles1692 Нет, совсем нет. Всегда есть много способов решения проблемы. Если вы хотите что-то элегантное, тогда вам подойдет этот вариант. - person GETah; 21.02.2012
comment
FileSystemWatcher определенно бесполезен для нас в течение многих лет; события изменения и удаления иногда дают сбои или не срабатывают, в результате чего нам приходится перетаскивать файлы, повторно выполнять действия и т. д. Это относительно, но в большинстве случаев с программным обеспечением 99% точности недостаточно. - person galaxis; 11.06.2021

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

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

Я считаю, что в большинстве случаев цикл while не запускается.

person Mike G    schedule 03.11.2015
comment
fi.Delete(); выдает исключение, если оно используется - person Mark Schultheiss; 02.10.2020

Облегченный код для использования 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);
    }
}
person JPK    schedule 31.08.2016

Вот код, использующий 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;
    }
}
person bradgonesurfing    schedule 09.07.2015

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

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

См. здесь и здесь

person Vadim    schedule 07.12.2017

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

person Valerie    schedule 24.09.2013

Удаление каталога с помощью Directory.Delete, в частности перегрузка, которая принимает 'рекурсивное' логическое значение в NTFS, должна быть атомарная операция с точки зрения вашей программы. Нет необходимости самостоятельно выполнять рекурсию.

person Chris Shain    schedule 20.02.2012
comment
Так и должно быть, но это не так. Directory.Exists иногда возвращает true, особенно если это следующая строка. Хуже того, Directory.Create иногда вызывает ошибку при вызове сразу после Directory.Delete. - person ILMTitan; 18.07.2017