正在等待系统删除文件

本文关键字:文件 删除 系统 在等待 | 更新日期: 2023-09-27 18:25:25

我在删除文件后刷新文件列表时遇到问题。当我发出删除文件的命令时,由于刷新方法试图访问应该删除的文件,所以引发了异常。

经过一些思考和调试,我得出结论,问题在于系统需要一些时间来删除文件。我是这样解决的:

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

而且效果很好。

我的问题是

有没有一种更优雅的方式来等待系统删除文件,然后继续使用代码。。。?

正在等待系统删除文件

这对我有效:

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循环都不会被输入。

使用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);
    }
}

我能想到的最优雅的方法是使用FileSystemWatcher并订阅其Deleted事件。

下面是一些使用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();
        }
    }
}

以及一些利用上述可观察到的优点的utils。

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

Directory.Delete将在遇到第一个错误时抛出异常。如果你想继续删除尽可能多的文件和子目录,那么你不应该使用Directory.Delete,应该在循环中使用try/catch块编写自己的递归删除。您可能想要这样做的一个示例是,如果您正在尝试清理临时文件,而其中一个文件已被锁定。

我一直使用这个:

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

请参阅此处和此处

从程序的角度来看,使用directory.Delete删除目录,特别是NTFS上采用"递归"布尔值的重载,应该是一个原子操作。不需要自己手动递归。