FileSystemWatcher 更改事件(对于“LastWrite”)不可靠

问题描述 投票:0回答:4

我试图在磁盘上更新文件时收到通知。我有兴趣在刷新发生时立即收到此通知,但是,FileSystemWatcher 似乎只会在流打开或关闭时发送事件。

在下面的代码中,我正在写入文件并重复将缓冲区刷新到磁盘。然而,FileSystemWatcher 仅在写入开始时和写入结束时通知我一次。

还有其他方式获取这些通知吗?或者我应该使用轮询吗?

代码:

class Program
{
    static void Main(string[] args)
    {
        FileSystemWatcher watcher = new FileSystemWatcher(Environment.CurrentDirectory, "Test.txt");
        watcher.Changed += watcher_Changed;
        watcher.EnableRaisingEvents = true;

        using(TextWriter writer = new StreamWriter("Test.txt"))
        {
            WriteData(writer);
            WriteData(writer);
            WriteData(writer);
            WriteData(writer);
        }

        Thread.Sleep(10000);
    }

    private static void WriteData(TextWriter writer)
    {
        writer.WriteLine("Hello!");
        writer.Flush();
        Console.WriteLine(DateTime.Now.ToString("T") + "] Wrote data");
        Thread.Sleep(3000);
    }

    static void watcher_Changed(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine(DateTime.Now.ToString("T") + "] Watcher changed!");
    }
}

更新1

我已更正 DateTime 的 ToString 函数以显示秒。这是上面代码的输出:

11:37:47 AM] Watcher changed!
11:37:47 AM] Wrote data
11:37:50 AM] Wrote data
11:37:53 AM] Wrote data
11:37:56 AM] Wrote data
11:37:59 AM] Watcher changed!

谢谢!

c# .net filesystemwatcher
4个回答
10
投票

与FileSystemWatcher无关。观察程序对文件系统 LastWrite 属性的更新做出反应。

例如NTFS 不会在每次写入时更新 LastWrite。该值会被缓存,并且仅在流关闭或其他某个未指定的时间时写入。 这份文件

时间戳会因各种原因在不同时间更新。关于文件时间戳的唯一保证是当进行更改的句柄关闭时正确反映文件时间。 [...] NTFS 文件系统将文件的上次访问时间的更新延迟至上次访问后最多 1 小时

我假设类似的缓存适用于写入


4
投票

我认为您的问题之一是所有写入都在事件获得该 CPU 时间的一部分之前执行。 MSDN 指出

当对受监视目录中的文件或目录的大小、系统属性、上次写入时间、上次访问时间或安全权限进行更改时,将引发 Changed 事件。

我做了一个测试,并在每次调用 WriteData(...) 后插入了 Sleeps,我得到的是

09:32] 观察者改变了!

09:32] 观察者改变了!

09:32] -----> 写入数据

09:32] -----> 写入数据

09:32] -----> 写入数据

09:32] -----> 写入数据

09:32] 观察者改变了!

09:32] 观察者改变了!

我想这证明了即使在调用 Flush() 之后立即被触发,这只是事件处理程序何时执行的问题(我假设它也对事件进行分组)。 我不知道你的项目的具体需求,但我不会投票。看起来很浪费,因为在我看来 FileSystemWatcher 做了你想要它做的事情。

编辑:好吧,我想当我发布这篇文章时我的大脑还没有准备好思考。 您的结论是,当您打开和关闭流时它会触发,这似乎更加合乎逻辑和正确。 我想我正在寻找“证明”当你调用flush时它会触发,因此找到了它 - 不知何故。

更新 我刚刚浏览了 USN-Journal,似乎也无法得到您想要的东西,因为它仅在文件关闭时才写入记录。 -> http://msdn.microsoft.com/en-us/library/aa363803(VS.85).aspx 我还发现了 C# 中的 USN-Viewer http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/c1550294-d121-4511-ac32-31551497f64e 读起来也可能很有趣.

我还运行了 DiskMon 以查看它是否实时获取更改。确实没有,但不知道是有意还是无意。然而,两者的问题是它们都需要管理员权限才能运行。 所以我猜你被 FileSystemWatcher 困住了。 (无论如何,您需要更新做什么,这不像您可以在文件被另一个程序打开/锁定时读取该文件。)

ps:我刚刚注意到你是 BugAid 的开发者,我最近才知道它 - 它看起来棒极了:)


2
投票

我通过使用计时器解决了这个问题:

private Timer _checkTimer;
private DateTime _lastSaved;

在初始化定时器之前,我获取文件的最后写入时间:

_lastSaved = File.GetLastWriteTime(_watchedFile);
_checkTimer = new Timer() { Interval = 1000, AutoReset = true };
_checkTimer.Elapsed += CheckTimer_Elapsed;
_checkTimer.Start();

然后,在每个经过的事件上,我检查最后一次写入时间是否比我知道的最后一次写入时间更新:

private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    DateTime lastWriteTime = File.GetLastWriteTime(_watchedFile);
    if (lastWriteTime > _lastSaved)
    {
       // Stop the timer to avoid concurrency problems
       _syncTimer.Stop();

       // Do here your processing

       // Now, this is the new last known write time
       _lastSaved = File.GetLastWriteTime(_watchedFile);

       // Start again the timer
       _syncTimer.Start();
    }
}

这就是我个人解决这个问题的方法,我在使用 FileSystemWatcher 时也遇到过这个问题。这种方法不像每秒读取文件内容或计算文件哈希来检查是否存在差异那么昂贵。


0
投票

在 Windows (10) 和 NTFS 上,如果您控制写入应用程序,您可以强制更新写入时间戳,它将被 FileSystemWatcher 拾取。

在 C#(和 .NET)中,您可以使用 FileStream.Flush(true)

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.