ChatGPT解决这个技术问题 Extra ChatGPT

FileSystemWatcher 与轮询以监视文件更改

我需要设置一个应用程序来监视在本地或网络驱动器上的目录中创建的文件。

FileSystemWatcher 或对计时器进行轮询将是最佳选择。我过去使用过这两种方法,但没有广泛使用。

这两种方法都有哪些问题(性能、可靠性等)?

FileSystemWatcher 是一个有漏洞的抽象,除了最基本的情况外,不能依赖任何东西。见这里:stackoverflow.com/a/22768610/129130
想要添加链接以供参考 this answer by Raymond Chen (Microsoft expert) on the topic of FileSystemWatcher's reliability。还有他的博客:The Old New Thing(例如搜索 FileSystemWatcher)。

J
Jason Jackson

我已经看到文件系统观察程序在生产和测试环境中失败。我现在认为它很方便,但我认为它不可靠。我的模式一直是使用文件系统观察程序来观察更改,但偶尔轮询以捕获丢失的文件更改。

编辑:如果您有 UI,您还可以让您的用户能够“刷新”更改而不是轮询。我会将它与文件系统观察程序结合起来。


我也见过跌倒的。我们使用的解决方案是包装我们自己的类,其中包装类还使用计时器来检查观察者是否仍在运行。
我们做了类似的事情——一旦我们处理了传递给 FileCreated 事件的文件,我们会在返回之前手动检查任何其他新文件。这似乎可以缓解大量文件同时到达时出现的任何问题。
我相信我们在 XP 和 Server 2003 中在本地目录和文件共享上对其进行了测试,并且在现场使用了 XP 机器。我们在本地目录和文件共享方面都遇到了问题。我们想出的可能原因之一是在目录中短时间内复制/创建了大量文件。
仅仅说“我有一天看到了鬼”既不是很有建设性也不是专业的。似乎人们在线程中,提到有关不可分页缓冲区溢出的 msdn 文档可以解释您的问题。您是否尝试过使用布伦特的方法?
我刚在亚马逊上买了一个气体传感器,令我惊讶的是有多少人说它不起作用,当他们显然没有正确校准它或者甚至不知道校准...... FileSystemWatcher 已经知道来自高流量的限制它的缓冲区大小。几乎可以保证这就是它“失败”的原因。这在文档中很容易解释,并且有一些变通方法可以提供非常可靠的操作(如下所示)。这不是一个很好的答案,只是说“错误,有一次没有工作,不知道为什么......没有人应该依赖它”。
佚名

我遇到的最大问题是缓冲区满时文件丢失。像馅饼一样容易修复 - 只需增加缓冲区。请记住,它包含文件名和事件,因此将其增加到预期的文件数量(试错)。它确实使用无法分页的内存,因此如果内存不足,它可能会强制其他进程进行分页。

这是有关缓冲区的 MSDN 文章:FileSystemWatcher..::.InternalBufferSize Property

根据 MSDN:

增加缓冲区大小是昂贵的,因为它来自无法换出到磁盘的非分页内存,因此请保持缓冲区尽可能小。为避免缓冲区溢出,请使用 NotifyFilter 和 IncludeSubdirectories 属性过滤掉不需要的更改通知。

我们使用 16MB,因为一次预计会有大批量。工作正常,从不错过文件。

我们还在开始处理甚至一个文件之前读取了所有文件……将文件名安全地缓存起来(在我们的例子中,缓存到数据库表中)然后处理它们。

对于文件锁定问题,我生成了一个进程,该进程等待文件被解锁等待一秒钟,然后是两秒钟,然后是四秒钟,等等。我们从不投票。这已经在生产中没有错误大约两年。


缓冲区溢出?哦,你的意思是堆栈溢出。
从 .NET 3.5 开始:“您可以将缓冲区设置为 4 KB 或更大,但不得超过 64 KB”
如果 FileSystemWatcher 的最大内部缓冲区为 64KB,您如何使用 16MB?
@ Jarvis,缓冲区是一个临时存储位置,配置为在传输信息直到可以处理之前保存信息,这通常意味着 FIFO 或队列,因为您希望按照请求到达的顺序处理请求,但是在某些过程中,例如程序中的递归使用的是 FILO 或 Stack 结构,在这种情况下,我们肯定指的是事件队列缓冲区,而不是程序调用堆栈缓冲区
petermeinl.wordpress.com/2015/05/18/tamed-filesystemwatcher 这篇博文分享了标准 FileSystemWatcher (FSW) 的强大包装器,解决了在实际应用程序中使用它来监控文件系统时经常遇到的问题。
b
bluish

如果排队更改的数量超出提供的缓冲区,FileSystemWatcher 也可能在繁忙时间错过更改。这不是 .NET 类本身的限制,而是底层 Win32 基础结构的限制。根据我们的经验,最小化此问题的最佳方法是尽快将通知出列并在另一个线程上处理它们。

正如上面@ChillTemp 所提到的,观察者可能无法在非 Windows 共享上工作。例如,它在安装的 Novell 驱动器上根本不起作用。

我同意一个很好的折衷办法是不定期地进行一次民意调查以找出任何错过的更改。


文件系统观察者可以开始快速连续引发大量事件。如果您的事件处理程序的执行速度至少与它们被触发的速度一样快,那么最终处理程序将开始将事件扔到地板上,您会错过一些事情。
c
chilltemp

另请注意,文件系统观察程序在文件共享上不可靠。特别是如果文件共享托管在非 Windows 服务器上。 FSW 不应该用于任何关键的事情。或者应该与偶尔的民意调查一起使用,以验证它没有遗漏任何东西。


Microsoft 是否承认它在非 Windows 文件共享上不可靠?自从从 Windows 共享切换到基于 Linux 的 SMB 共享后,我们当然正在亲身体验这一点。
不是我知道的。而且我确信这只是不同供应商之间的相互指责游戏。
我们在映射驱动器上遇到了文件系统观察程序的问题。如果地图断开连接然后重新连接,则文件观察程序不再引发更改。很容易解决,但仍然是对文件系统观察者恕我直言的打击。
b
bluish

就我个人而言,我在生产系统上使用了 FileSystemWatcher,它运行良好。在过去的 6 个月中,它没有出现过 24x7 全天候运行的问题。它正在监视单个本地文件夹(共享)。我们必须处理的文件操作数量相对较少(每天触发 10 个事件)。这不是我曾经担心过的事情。如果我不得不重新做出决定,我会再次使用它。


b
bluish

我目前在平均每 100 毫秒更新一次的 XML 文件上使用 FileSystemWatcher

我发现只要正确配置了 FileSystemWatcher,您就不会遇到 local 文件的问题。

我没有远程文件观看和非 Windows 共享的经验。

我会认为轮询文件是多余的,不值得开销,除非您天生不信任 FileSystemWatcher 或直接经历了此处其他所有人列出的限制(非 Windows 共享和远程文件监视)。


b
bluish

我在网络共享上使用 FileSystemWatcher 时遇到了麻烦。如果您在纯 Windows 环境中,这可能不是问题,但我正在观看 NFS 共享,并且由于 NFS 是无状态的,因此当我正在观看的文件发生更改时,从来没有通知。


我遇到了同样的问题,但令我意外的是 FileSystemWatcher 位于使用 NFS 共享文件夹的同一 Windows 服务器上。与 NFS 共享文件夹的事实导致 filesystemwatcher 看不到远程使用共享创建的文件(即来自映射共享的 Linux),而如果我在受监视的同一文件夹上写入文件,则会触发 filesystemwatcher。看起来 NFS 服务器使用较低层写入文件,并且触发文件系统观察器的 api 层没有参与,有人有更多信息吗?
@Mosè 我也面临同样的问题。你有什么解决办法吗?
并不是真正解决问题的方法,但作为解决方法,我最终(可悲)定期比较文件系统结构的差异并自己生成相关事件,正确选择数据结构并没有那么慢,只是有点压力在列表的文件系统上
b
bluish

我会去投票。

网络问题导致 FileSystemWatcher 不可靠(即使在超载错误事件时也是如此)。


T
Treb

我在网络驱动器上使用 FSW 时遇到了一些大问题:删除文件总是抛出错误事件,而不是删除事件。我没有找到解决方案,所以我现在避免使用 FSW 并使用轮询。

另一方面,创建事件运行良好,因此如果您只需要注意文件创建,则可以使用 FSW。

此外,无论是否共享,我在本地文件夹上都没有问题。


s
spludlow

尽快从事件方法返回,使用另一个线程,为我解决了这个问题:

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
    Task.Run(() => MySubmit(e.FullPath));
}

T
ThunderGr

在我看来,同时使用 FSW 和轮询是浪费时间和资源,我很惊讶有经验的开发人员建议这样做。如果您需要使用轮询来检查任何“FSW 未命中”,那么您自然可以完全丢弃 FSW 并仅使用轮询。

目前,我正在尝试决定是否将使用 FSW 或轮询来开发我开发的项目。阅读答案,很明显在某些情况下 FSW 可以完美地满足需求,而其他时候则需要轮询。不幸的是,没有答案实际上处理了性能差异(如果有的话),只有“可靠性”问题。有没有人可以回答这部分问题?

编辑:nmclean 关于同时使用 FSW 和轮询的有效性的观点(如果您有兴趣,可以阅读评论中的讨论)似乎是一个非常合理的解释,为什么在某些情况下同时使用 FSW 和轮询是有效的。感谢您为我(以及其他任何有相同观点的人)阐明这一点,nmclean。


如果您想尽快响应文件更改怎么办?例如,如果您每分钟轮询一次,则在文件更改和应用程序接收更改之间可能会有多达 1 分钟的延迟。 FSW 事件大概会在此之前触发。因此,通过使用这两种方法,您可以尽可能少地处理事件,但如果有任何错过的事件,也可以捡起。
@rom99 正是我的观点。如果 FSW 在您需要快速响应的情况下不可靠,那么使用它是没有意义的,因为您将遇到无法快速响应的情况,因此您的应用程序将不可靠。您需要在线程中以较短的间隔轮询。两者都做意味着您对轮询所涵盖的响应时间有一定的容忍度,那么,为什么不只使用轮询呢?
@ThunderGr “因此,您的应用程序将不可靠。” - 在许多情况下,速度并不是可靠性的先决条件。工作必须完成,但可以等待一段时间。如果我们将缓慢、可靠的轮询与快速、不可靠的 FSW 结合起来,我们会得到一个始终可靠且有时快速的应用程序,这比可靠且从不快速的应用程序要好。我们可以通过持续轮询来移除 FSW 并获得相同的最大响应时间,但这是以牺牲应用程序其余部分的响应能力为代价的,因此只有在绝对需要立即响应时才应该这样做。
现在为什么上面的论点很糟糕?因为,虽然我们仍然需要磁盘访问,但我们需要的更少。同样,您可以减少轮询。仅仅因为我们仍然检查所有文件并不意味着工作量是相同的。您的说法“无论是否使用 FSW,轮询在 CPU 时间上都很昂贵”是错误的。通过将“即时性”问题转移到 FSW,我们可以将轮询更改为空闲的、低优先级的任务,从而在任何给定时间大幅降低应用程序的繁忙度,同时仍然提供即时性的“待遇”。您根本无法仅通过轮询来实现相同的平衡。
@nmclean 感谢您花时间和精力以您的方式澄清这一点。当你这样说时,它肯定更有意义。就像有时缓存不适合您的特定问题一样,FSW(当它被证明不可靠时)可能不适合。事实证明,你一直都是对的。很抱歉我花了这么多时间才得到它。
b
bluish

使用创建事件而不是更改的工作解决方案

即使是复制、剪切、粘贴、移动。

class Program
{        

        static void Main(string[] args)
        {
            string SourceFolderPath = "D:\\SourcePath";
            string DestinationFolderPath = "D:\\DestinationPath";
            FileSystemWatcher FileSystemWatcher = new FileSystemWatcher();
            FileSystemWatcher.Path = SourceFolderPath;
            FileSystemWatcher.IncludeSubdirectories = false;
            FileSystemWatcher.NotifyFilter = NotifyFilters.FileName;   // ON FILE NAME FILTER       
            FileSystemWatcher.Filter = "*.txt";         
             FileSystemWatcher.Created +=FileSystemWatcher_Created; // TRIGGERED ONLY FOR FILE GOT CREATED  BY COPY, CUT PASTE, MOVE  
            FileSystemWatcher.EnableRaisingEvents = true;

            Console.Read();
        }     

        static void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {           
                string SourceFolderPath = "D:\\SourcePath";
                string DestinationFolderPath = "D:\\DestinationPath";

                try
                {
                    // DO SOMETING LIKE MOVE, COPY, ETC
                    File.Copy(e.FullPath, DestinationFolderPath + @"\" + e.Name);
                }
                catch
                {
                }          
        }
}

使用静态存储的文件属性更改事件时此文件监视器的解决方案

class Program
{
    static string IsSameFile = string.Empty;  // USE STATIC FOR TRACKING

    static void Main(string[] args)
    {
         string SourceFolderPath = "D:\\SourcePath";
        string DestinationFolderPath = "D:\\DestinationPath";
        FileSystemWatcher FileSystemWatcher = new FileSystemWatcher();
        FileSystemWatcher.Path = SourceFolderPath;
        FileSystemWatcher.IncludeSubdirectories = false;
        FileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;          
        FileSystemWatcher.Filter = "*.txt";         
        FileSystemWatcher.Changed += FileSystemWatcher_Changed;
        FileSystemWatcher.EnableRaisingEvents = true;

        Console.Read();
    }     

    static void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        if (e.Name == IsSameFile)  //SKIPS ON MULTIPLE TRIGGERS
        {
            return;
        }
        else
        {
            string SourceFolderPath = "D:\\SourcePath";
            string DestinationFolderPath = "D:\\DestinationPath";

            try
            {
                // DO SOMETING LIKE MOVE, COPY, ETC
                File.Copy(e.FullPath, DestinationFolderPath + @"\" + e.Name);
            }
            catch
            {
            }
        }
        IsSameFile = e.Name;
    }
}

这是针对多触发事件这一问题的变通解决方案。


u
user2819502

我会说使用轮询,尤其是在 TDD 场景中,因为在触发轮询事件时模拟/存根文件的存在或其他情况比依赖更“不受控制”的 fsw 事件要容易得多。 + 在许多受 fsw 错误困扰的应用程序上工作过。