C# FileSystemWatcher对子目录中的更改不可靠

C# FileSystemWatcher对子目录中的更改不可靠,c#,.net,filesystemwatcher,C#,.net,Filesystemwatcher,我目前正在为OpenFOAM输出文件实现文件内容监视程序。这些文件在Unix环境中由OpenFOAM编写,在Windows环境中由我的应用程序使用 请考虑我的第一个收敛文件的工作守望者(这些文件在每次迭代后得到更新): 这正如预期的那样有效。每次在监视目录中更改与模式匹配的文件时(记事本++会通知我该文件也已更改),都会处理这些文件 从这个简单的“所有文件都在一个目录中”场景开始,我开始为不同类型的文件构建一个监视程序(对于熟悉OpenFOAM的人,强制函数对象)。这些文件保存在分层文件夹结构中

我目前正在为OpenFOAM输出文件实现文件内容监视程序。这些文件在Unix环境中由OpenFOAM编写,在Windows环境中由我的应用程序使用

请考虑我的第一个收敛文件的工作守望者(这些文件在每次迭代后得到更新):

这正如预期的那样有效。每次在监视目录中更改与模式匹配的文件时(记事本++会通知我该文件也已更改),都会处理这些文件

从这个简单的“所有文件都在一个目录中”场景开始,我开始为不同类型的文件构建一个监视程序(对于熟悉OpenFOAM的人,强制函数对象)。这些文件保存在分层文件夹结构中,如下所示:

NameOfFunctionObject
|_StartTimeOfSolutionSetup#1
| |_forces.dat
|_StartTimeOfSolutionSetup#2
  |_forces.dat
我的目标是从“NameOfFunctionObject”中读取所有forces.dat,并对包含的所有数据进行一些处理。此外,我也喜欢有机会阅读和观看一个文件。因此,我的实现(大量借鉴了上述内容)目前看起来如下:

FileSystemWatcher watcher;
if (isSingleFile)
    watcher = new FileSystemWatcher(Directory.GetParent(WatchPath).ToString(), Path.GetFileName(WatchPath));
else
    watcher = new FileSystemWatcher(WatchPath, "forces.dat");
watcher.IncludeSubdirectories = !isSingleFile;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Attributes | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.DirectoryName | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.Security;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Created;
watcher.Deleted += Watcher_Deleted;
watcher.Error += Watcher_Error;
watcher.Renamed += Watcher_Renamed;
watcher.EnableRaisingEvents = isWatchEnabled;
因此,根据我是只想查看一个文件还是多个文件,我设置了要查看的目录和文件过滤器。如果我观看多个文件,我会将观看者设置为同时观看子目录。由于严格的测试,我过滤所有通知并捕获所有观察者事件

如果我测试单文件选项,一切都会按预期工作,对文件的更改会被正确地报告和处理(同样,使用可靠的旧记事本++检查工作正常) 不过,在测试多文件选项时,事情变得复杂了。 文件路径正确,初始读取工作正常。但两个观察者事件都没有触发。奇怪的是:记事本++仍在响,说文件已更改,Windows资源管理器显示新的文件日期和新的文件大小。如果我将文件保存在Notepad++中,则会触发监视程序。如果我创建了一个新的文件来匹配监视目录中的模式(顶层或底层都不重要!),那么监视程序就会被触发。甚至在观察一个过滤器。捕获临时文件的创建不会触发,因此可以安全地假设没有创建临时文件


通常,观察者的行为与预期一致,它可以检测单个文件的更改,它可以检测根被观察文件夹及其子文件夹中的文件创建。一旦文件位于子文件夹中,它就无法识别对该文件的非windows更改。这种行为是故意的吗?更重要的是:我如何才能在不使用计时器和手动轮询的情况下优雅地处理它呢?

我想这可能与您有关

FileSystemWatcher
使用带有几个相关标志的Winapi调用

当您第一次调用
ReadDirectoryChangesW
时,系统将分配一个 用于存储更改信息的缓冲区。此缓冲区与 目录句柄,直到它关闭且其大小不变 在它的有生之年。调用之间发生的目录更改 此函数被添加到缓冲区,然后与下一个 打电话如果缓冲区溢出,则缓冲区的全部内容都将被删除 丢弃

FileSystemWatcher
中的模拟是属性

备注您可以将缓冲区设置为4KB或更大,但不能设置为4KB或更大 超过64 KB。如果尝试将InternalBufferSize属性设置为 小于4096字节时,您的值将被丢弃,并且 InternalBufferSize属性设置为4096字节。最好 性能,在基于英特尔的计算机上使用4KB的倍数

系统通知组件文件更改,并存储这些更改 组件创建并传递给API的缓冲区中的更改每个 事件最多可使用16个字节的内存,不包括文件名。 如果在短时间内有许多更改,缓冲区可能会溢出。 这会导致组件无法跟踪目录中的更改, 它只会提供全面的通知。增大 缓冲区可以防止丢失文件系统更改事件。然而, 增加缓冲区大小是昂贵的,因为它来自非分页 无法交换到磁盘的内存,因此请保持缓冲区尽可能小 尽可能的要避免缓冲区溢出,请使用NotifyFilter和 IncludeSubdirectories属性以过滤掉不需要的更改 通知


如果情况变得更糟,你可以混合使用轮询和跟踪,它已经帮我摆脱了麻烦好几次了

有很多变化吗?或者我们只讨论了几个文件(当它不工作时)目前有6个文件正在被监视,其中一个文件每分钟都会发生变化,因此这不是一个高频率的问题我感到痛苦,尝试增加缓冲区,但根据我的经验,filesystemwatcher可能天生不可靠,您可能也想进行轮询我现在增加了缓冲区(没有用),但启动一个间隔为5分钟的计时器以触发轮询作为备份。如果观察者检测到变化,它将重置计时器。因此,它不像轮询,更像是强制重新加载。因为它不会是非常大的文件,所以我应该很好。非常感谢。
FileSystemWatcher watcher;
if (isSingleFile)
    watcher = new FileSystemWatcher(Directory.GetParent(WatchPath).ToString(), Path.GetFileName(WatchPath));
else
    watcher = new FileSystemWatcher(WatchPath, "forces.dat");
watcher.IncludeSubdirectories = !isSingleFile;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Attributes | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.DirectoryName | NotifyFilters.LastAccess | NotifyFilters.CreationTime | NotifyFilters.Security;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Created;
watcher.Deleted += Watcher_Deleted;
watcher.Error += Watcher_Error;
watcher.Renamed += Watcher_Renamed;
watcher.EnableRaisingEvents = isWatchEnabled;