.net FileSystemWatcher已更改事件(针对“LastWrite”)不可靠

baubqpgj  于 2023-03-24  发布在  .NET
关注(0)|答案(4)|浏览(159)

我试图在磁盘上更新文件时获得通知。我对刷新发生时立即获得此通知感兴趣,然而,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!

谢谢!

ne5o7dgx

ne5o7dgx1#

它与FileSystemWatcher无关。监视器对文件系统的LastWrite属性的更新做出React。
例如,NTFS不会在每次写入时更新LastWrite。该值被缓存,只有在流关闭或其他未指定的时间才被写入。This document
时间戳在不同的时间和不同的原因被更新。关于文件时间戳的唯一保证是,当进行更改的句柄被关闭时,文件时间被正确地反映出来。[...] NTFS文件系统将文件的最后访问时间的更新延迟到最后访问后1小时
我假设类似的缓存适用于写操作

rbl8hiat

rbl8hiat2#

我认为您的问题之一是,在事件获得该cpu-time的一部分之前,您的所有写入都已执行。MSDN states
当对所监视目录中的文件或目录的大小、系统属性、上次写入时间、上次访问时间或安全权限进行更改时,将引发Changed事件。
我做了一个测试,在每次调用WriteData(...)后插入Sleeps,得到的结果是
09:32]观察者变了!
09:32]观察者变了!
09:32] -----〉写入数据
09:32] -----〉写入数据
09:32] -----〉写入数据
09:32] -----〉写入数据
09:32]观察者变了!
09:32]观察者变了!
我想这证明了even在你调用Flush()之后就被触发了,这只是一个事件处理程序何时被执行的问题(我假设它也对事件进行分组)。我不知道你的项目的具体需求,但我不会轮询。在我看来,这似乎是一种浪费,因为FileSystemWatcher做了你想让它做的事情。
编辑:好吧,我猜我的大脑还没有完全准备好思考,当我发布这篇文章时。你的结论是,当你打开和关闭流时,它会触发,这似乎更符合逻辑和正确。我想我是在寻找“证明”,当你调用flush时,它会触发,因此找到了它-不知何故。

更新我刚刚在USN日志戳了戳,似乎不会得到你想要的,因为它只在文件关闭时写入记录。-〉http://msdn.microsoft.com/en-us/library/aa363803(VS.85).aspx我还发现USN-Viewer in C#http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/c1550294-d121-4511-ac32-31551497f64e可能也很有趣。

我还运行了DiskMon,看看它是否能实时获取更改。它没有,但我不知道这是不是故意的。然而,这两个的问题是,它们都需要管理员权限才能运行。所以我猜你被FileSystemWatcher卡住了。(无论如何,你需要更新的是什么,当它被另一个程序打开/锁定时,你不可能读取文件。)
ps:我刚刚注意到你是BugAid的开发者,我最近才意识到-它看起来很棒:)

nmpmafwu

nmpmafwu3#

我知道这是一个旧的线程,但问题仍然存在10年后。我解决了这个问题,使用计时器:

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.这种方法是不一样昂贵的阅读文件内容或计算文件哈希每一秒来检查是否有差异.

vyu0f0g1

vyu0f0g14#

在Windows(10)和NTFS上,如果您控制写入应用程序,您可以强制更新写入时间戳,它将被FileSystemWatcher拾取。
在C#(和.NET)中,可以使用FileStream.Flush(true)

相关问题