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

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

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

评论

FileSystemWatcher是一个泄漏性抽象,除了最基本的情况外,其他任何情况都不能依赖FileSystemWatcher。看到这里:stackoverflow.com/a/22768610/129130

要添加一个链接,以供参考Raymond Chen(Microsoft专家)关于FileSystemWatcher可靠性的主题的答案。还有他的博客:The Old New Thing(例如搜索FileSystemWatcher)。

#1 楼

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

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

评论


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

–乔尔·科恩(Joel Coehoorn)
08-10-27在14:16

我们做类似的事情-处理完传递给FileCreated事件的文件后,我们将在返回之前手动检查所有其他新文件。这似乎可以缓解许多文件一次到达时发生的任何问题。

– John Sible
08-10-27在14:19

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

–Jason Jackson
08-10-27在17:26

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

–v.oddou
2014年9月30日在7:47

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

–u8it
16年7月29日在16:24

#2 楼

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

这是有关缓冲区的MSDN文章:
FileSystemWatcher .. ::。InternalBufferSize属性

每个MSDN:


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


由于一次要批量生产,我们使用16MB。工作正常,永远不会丢失文件。

我们甚至在开始处理一个文件之前就已读取了所有文件...将文件名安全地缓存掉(在我们的情况下,保存到数据库表中)然后进行处理他们。

对于文件锁定问题,我产生了一个过程,该过程等待文件被解锁,等待一秒钟,然后是两个,然后是四个,等等。我们从不投票。这个产品已经投入生产了大约两年了。

评论


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

–罗宾·罗德里克斯(Robin Rodricks)
09-10-19在4:42

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

–brad
2013年6月15日在1:01



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

– B.K.
2013年9月13日在2:26

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

–MikeT
2013年9月26日10:31



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

– Kiquenet
18年7月18日在20:47

#3 楼

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

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

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

评论


文件系统监视程序可以快速连续地引发很多事件。如果您不能至少像触发事件一样快地执行事件处理程序,最终处理程序将开始在地板上放下事件,您会错过一切。

–布伦特·洛克伍德(Brent Rockwood)
2012年2月3日,12:39

#4 楼

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

评论


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

– Sean
2012年4月26日15:21

不是我知道的。而且我敢肯定,这将只是不同供应商之间的一种责备游戏。

– chilltemp
2012年5月2日在22:01

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

–理查德·多曼(Richard Dorman)
13年5月24日在15:17

#5 楼

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

#6 楼

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

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

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

除非您固有地不信任FileSystemWatcher或直接体验过,否则我认为轮询文件是多余的,不值得开销。其他所有人列出的限制(非Windows共享和远程文件监视)。

#7 楼

我会使用轮询。

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

#8 楼

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

评论


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

–MosèBottacini
18年5月31日在13:20



#9 楼

我在网络驱动器上的FSW遇到了一些大问题:删除文件始终会引发错误事件,而不会引发已删除事件。我没有找到解决方案,所以我现在避免使用FSW并使用轮询。另一方面,创建事件也可以正常工作,因此,如果您只需要监视文件创建,则可以进行FSW。

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

#10 楼

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

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


#11 楼

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

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

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

评论


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

–rom99
2014年1月7日,12:15

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

– ThunderGr
2014年1月8日在7:16

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

–nmclean
2014年1月28日在17:25

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

–nmclean
2014年1月30日18:21

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

– ThunderGr
2014年1月31日上午7:14

#12 楼

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

甚至用于复制,剪切,粘贴,移动。

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


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

#13 楼

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