FileSystemWatcher与轮询以监视文件更改


152

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

FileSystemWatcher计时器上的或轮询将是最佳选择。我过去曾经使用过这两种方法,但并未广泛使用。

两种方法都存在哪些问题(性能,可靠性等)?


3
FileSystemWatcher是一个泄漏性的抽象,除了最基本的情况以外,不能依靠它。看到这里:stackoverflow.com/a/22768610/129130
SteinÅsmul2014年

1
要添加一个链接,以供参考Raymond Chen(Microsoft专家)关于FileSystemWatcher的可靠性的答案。还有他的博客:The Old New Thing(例如搜索FileSystemWatcher)。
斯坦·奥斯穆尔(SteinÅsmul),

Answers:


105

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

编辑:如果您有UI,还可以使您的用户“刷新”更改而不是轮询。我将其与文件系统监视程序结合在一起。


11
我也看过跌倒了。我们使用的解决方案是包装我们自己的类,包装类ALSO使用计时器来检查观察者是否还在运行。
乔尔·科洪

我们做类似的事情-处理完传递给FileCreated事件的文件后,我们将在返回之前手动检查所有其他新文件。这似乎可以减轻因同时到达大量文件而引起的任何问题。
John Sible

4
我相信我们已经在XP和Server 2003的本地目录和文件共享上对其进行了测试,并在现场安装了XP计算机。我们在本地目录和文件共享方面都遇到了问题。我们想出的可能原因之一是在短时间内在目录中复制/创建了很多文件。
杰森·杰克逊

5
只说“我有一天见过鬼”不是很有建设性或专业性的。似乎有些人提起该线程,提到msdn文档中有关无法分页的缓冲区溢出的问题,可以解释您的问题。您是否尝试过使用布伦特的方法?
v.oddou 2014年

4
我刚刚在亚马逊上购买了一个气体传感器,当他们明显没有正确校准或什至不知道校准的时候,有很多人说它不起作用使我感到惊讶。它的缓冲区大小。几乎可以保证,这就是它“失败”的原因。这在文档中很容易解释,并且有一些解决方法可以提供非常可靠的操作(如下所述)。这不是一个好答案,只是说“ errr,那一次有什么不起作用,不确定为什么...没人应该依靠它”。
u8it '16

60

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

这是有关buffer的MSDN文章: FileSystemWatcher .. ::。InternalBufferSize属性

每个MSDN:

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

我们使用16MB,因为一次可能会有大量批量。正常工作,永远不会丢失任何文件。

在开始处理一个文件之前,我们还读取了所有文件...将文件名安全地缓存掉(在本例中为数据库表),然后对其进行处理。

对于文件锁定问题,我产生了一个过程,该过程等待文件被解锁,等待一秒钟,然后是两个,然后是四个,等等。我们从不投票。这已经投入生产了两年左右,没有出现错误。


12
缓冲区溢出?哦,你的意思是堆栈溢出。
TheFlash

1
从.NET 3.5开始:“您可以将缓冲区设置为4 KB或更大,但不能超过64 KB”
brad 2013年

9
如果FileSystemWatcher的最大内部缓冲区为64KB,则如何使用16MB?
BK 2013年

1
@ Jarvis,缓冲区是临时存储位置,配置为在信息被传输之前一直保存到可以处理为止,这通常意味着您要按到达顺序处理请求时使用FIFO或队列,但是在某些过程中(例如程序中的递归)使用的是FILO或Stack结构,在这种情况下,我们肯定是指事件队列缓冲区,而不是程序调用堆栈缓冲区
MikeT 2013年

1
petermeinl.wordpress.com/2015/05/18/tamed-filesystemwatcher 这篇文章分享了有关标准FileSystemWatcher(FSW)的可靠包装,这些包装解决了在实际应用中使用它来监视文件系统时通常遇到的问题。
Kiquenet '18年

35

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

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

我同意,一个不错的折衷方案是偶尔进行民意调查以获取所有遗漏的更改。


4
文件系统观察程序可以快速连续地引发很多事件。如果您不能至少按照触发事件的速度执行事件处理程序,最终该处理程序将开始在地板上放下事件,您会错过一切。
布伦特·洛克伍德

17

另请注意,文件系统监视程序在文件共享上不可靠。特别是如果文件共享托管在非Windows服务器上。FSW不应用于任何紧急情况。或应与偶尔的民意调查一起使用,以确认它没有遗漏任何东西。


3
Microsoft是否已承认在非Windows文件共享上不可靠?从Windows共享切换到基于Linux的SMB共享,​​我们当然已经经历了第一手资料。
肖恩

1
不是我知道的。而且我敢肯定,这将是不同供应商之间的非常规游戏。
chilltemp 2012年

1
我们在映射驱动器上遇到了文件系统监视程序的问题。如果地图断开连接,然后重新连接,则文件监视程序将不再引发更改。易于解决,但仍对文件系统监视程序恕我直言。
理查德·多曼

11

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


7

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

我发现只要FileSystemWatcher配置正确,本地文件就永远不会出现问题。

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

我认为轮询文件是多余的,不值得花费额外费用,除非您固有地不信任该文件,FileSystemWatcher或者直接经历了此处其他所有人列出的限制(非Windows共享和远程文件监视)。


5

我会去投票。

网络问题导致FileSystemWatcher不稳定(即使在错误事件超载时)。


5

FileSystemWatcher在网络共享上使用时遇到麻烦。如果您使用的是纯Windows环境,则可能不是问题,但是我正在查看NFS共享,并且由于NFS是无状态的,所以当我查看的文件发生更改时,永远不会有通知。


我遇到了同样的问题,但是这对我来说是意外的,因为FileSystemWatcher位于使用NFS共享文件夹的同一Windows服务器上。与NFS共享文件夹的事实导致filesystemwatcher无法远程查看使用该共享创建的文件(即,从映射该共享的Linux),而如果我在受监视的同一文件夹中写入文件,则会触发filesystemwatcher。看起来NFS服务器使用较低的层和api层来写入文件,这会触发filesystemwatcher未参与,有人有更多信息吗?
Mosè地区Bottacini

3

我在网络驱动器上的FSW遇到了一些大问题:删除文件始终会引发错误事件,而不会引发已删除事件。我没有找到解决方案,因此现在避免使用FSW并使用轮询。

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

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


3

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

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

2

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

目前,我正在尝试决定是对开发的项目使用FSW 还是进行轮询。阅读答案,很明显,在某些情况下,FSW可以完美满足需求,而在其他情况下,则需要轮询。不幸的是,实际上没有答案能解决性能差异(如果有的话),而仅涉及“可靠性”问题。有谁能回答问题的那一部分?

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


1
如果要尽快响应文件更改怎么办?例如,如果您每分钟轮询一次,则在文件更改与应用程序开始更改之间可能会有多达1分钟的延迟。大概在那之前会触发FSW事件。因此,通过使用这两种方法,您将以尽可能少的延迟处理事件,如果有的话,还可以选择丢失的事件。
2014年

@ rom99正是我的意思。如果在需要快速响应的情况下FSW不可靠,则没有用处,因为在某些情况下,您将无法快速响应,因此您的应用程序将不可靠。您需要做的是在线程中以较短的时间间隔进行轮询。两者都做,意味着您在轮询覆盖的响应时间上有容忍度,那么,为什么不只使用轮询呢?
ThunderGr 2014年

5
@ThunderGr “因此,您的应用程序将不可靠。” -在许多情况下,速度并不是可靠性的先决条件。该工作必须完成,但可能要等待一段时间。如果将慢速,可靠的轮询与快速,不可靠的 FSW结合使用,我们将获得一个始终可靠有时甚至是快速的应用程序,这比可靠且永不过时的性能要好。我们可以通过进行恒定轮询来删除FSW并获得相同的最大响应时间,但这是以牺牲应用程序其余部分的响应能力为代价的,因此仅在绝对需要立即响应的情况下才应这样做。
nmclean 2014年

2
现在,为什么上面的说法很糟糕?因为,尽管我们仍然需要磁盘访问,但是却需要更少的磁盘。同样,您可以减少轮询。仅仅因为我们仍然检查所有文件并不意味着工作量是相同的。您的说法“使用FSW或不使用FSW轮询会占用大量的CPU时间”,这是错误的。通过将FSW的“即时性”问题转移出去,我们可以将轮询更改为空闲的低优先级任务,从而在给定时间显着减少应用程序的繁忙度,同时仍然提供即时性的“处理”。您仅靠轮询根本无法达到相同的平衡。
nmclean 2014年

9
@nmclean感谢您花费时间和精力来按照您的方式进行澄清。当你这样说的时候,这肯定更有意义。就像有时候缓存不适合您的特定问题一样,FSW(在证明不可靠时)可能不适合。事实证明,你一直都是对的。对不起,我花了这么多时间才得到它。
ThunderGr 2014年

1

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

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

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

这是解决多重触发事件问题的解决方法。


0

我会说使用轮询,尤其是在TDD方案中,因为模拟/存根文件的存在或以其他方式触发轮询事件比依赖更“不受控制的” fsw事件要容易得多。+曾经从事过许多受fsw错误困扰的应用程序。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.