我需要设置一个应用程序,该程序监视本地或网络驱动器中目录中正在创建的文件。
FileSystemWatcher
计时器上的或轮询将是最佳选择。我过去曾经使用过这两种方法,但并未广泛使用。
两种方法都存在哪些问题(性能,可靠性等)?
我需要设置一个应用程序,该程序监视本地或网络驱动器中目录中正在创建的文件。
FileSystemWatcher
计时器上的或轮询将是最佳选择。我过去曾经使用过这两种方法,但并未广泛使用。
两种方法都存在哪些问题(性能,可靠性等)?
Answers:
我已经看到文件系统监视程序在生产和测试环境中失败。我现在认为它很方便,但我认为它不可靠。我的模式是使用文件系统监视程序监视更改,但偶尔进行轮询以捕获丢失的文件更改。
编辑:如果您有UI,还可以使您的用户“刷新”更改而不是轮询。我将其与文件系统监视程序结合在一起。
我遇到的最大问题是缓冲区已满时丢失文件。容易修复-只需增加缓冲区即可。请记住,它包含文件名和事件,因此请将其增加到预期的文件量(尝试和错误)。它确实使用了无法分页的内存,因此如果内存不足,它可能会强制其他进程进行分页。
这是有关buffer的MSDN文章: FileSystemWatcher .. ::。InternalBufferSize属性
每个MSDN:
增加缓冲区大小是昂贵的,因为它来自无法调换到磁盘的非分页内存,因此,请保持缓冲区尽可能小。为避免缓冲区溢出,请使用NotifyFilter和IncludeSubdirectories属性过滤掉不需要的更改通知。
我们使用16MB,因为一次可能会有大量批量。正常工作,永远不会丢失任何文件。
在开始处理一个文件之前,我们还读取了所有文件...将文件名安全地缓存掉(在本例中为数据库表),然后对其进行处理。
对于文件锁定问题,我产生了一个过程,该过程等待文件被解锁,等待一秒钟,然后是两个,然后是四个,等等。我们从不投票。这已经投入生产了两年左右,没有出现错误。
的FileSystemWatcher
也可能错过在繁忙时间的变化,如果排队改变的次数溢出提供的缓冲液中。这不是对.NET类本身的限制,而是对底层Win32基础结构的限制。根据我们的经验,最小化此问题的最佳方法是尽快使通知出队并在另一个线程上处理它们。
如上面@ChillTemp所述,观察者可能无法在非Windows共享上工作。例如,它根本无法在已安装的Novell驱动器上工作。
我同意,一个不错的折衷方案是偶尔进行民意调查以获取所有遗漏的更改。
另请注意,文件系统监视程序在文件共享上不可靠。特别是如果文件共享托管在非Windows服务器上。FSW不应用于任何紧急情况。或应与偶尔的民意调查一起使用,以确认它没有遗漏任何东西。
FileSystemWatcher
在网络共享上使用时遇到麻烦。如果您使用的是纯Windows环境,则可能不是问题,但是我正在查看NFS共享,并且由于NFS是无状态的,所以当我查看的文件发生更改时,永远不会有通知。
同时使用FSW 和在我看来,同时轮询会浪费时间和资源,令我感到惊讶的是,经验丰富的开发人员建议这样做。如果您需要使用轮询来检查是否有任何“ FSW未命中”,那么自然可以完全丢弃FSW,而仅使用轮询。
目前,我正在尝试决定是对开发的项目使用FSW 还是进行轮询。阅读答案,很明显,在某些情况下,FSW可以完美满足需求,而在其他情况下,则需要轮询。不幸的是,实际上没有答案能解决性能差异(如果有的话),而仅涉及“可靠性”问题。有谁能回答问题的那一部分?
编辑:nmclean关于同时使用FSW和轮询的有效性的观点(如果您有兴趣,可以阅读评论中的讨论)似乎是一个非常合理的解释,为什么有些情况下同时使用FSW和轮询是高效。感谢您为我(以及其他持相同观点的人)提供帮助,nmclean。
用于创建事件而不是更改的工作解决方案
即使是复制,剪切,粘贴,移动。
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;
}
}
这是解决多重触发事件问题的解决方法。