【问题标题】:Improving the Performance of FileSystemWatcher提高 FileSystemWatcher 的性能
【发布时间】:2016-07-05 09:18:17
【问题描述】:

我有以下代码注册FileSystemWatcher,然后在指定文件上发生写入事件时运行一些方法DoRequiedWork()。还有一些逻辑可以防止在每次写入时触发多个事件(使用lastReadlastWriteTime):

// To prevent multiple events from firing
static DateTime lastRead = DateTime.MinValue;
static string UserName = GetUserName();

private static void Run()
{
    // Create a new FileSystemWatcher and set its properties.
    var watcher = new FileSystemWatcher
    {
        Path = $@"\\file\home$\{UserName}\Application Data",
        NotifyFilter =  
            NotifyFilters.LastWrite,
        Filter = "filetowatch.txt"
    };

    // Activate
    watcher.Changed += OnChanged;
    watcher.EnableRaisingEvents = true;

    while (true)
    {
        System.Threading.Thread.Sleep(10000);
    }
}

private static void OnChanged(object source, FileSystemEventArgs e)
{
    var lastWriteTime = File.GetLastWriteTime(e.FullPath);

    if (lastWriteTime == lastRead) return;

    DoRequiredWork();

    lastRead = lastWriteTime; 
}

我想使用c++ api ReadDirectoryChangesW 来实现这一点,以尝试提高性能,但我不知道该怎么做。

查看pinvoke,可以看到签名可以定义为:

[DllImport("kernel32.dll")]
 static extern bool ReadDirectoryChangesW(IntPtr hDirectory, IntPtr lpBuffer,
    uint nBufferLength, bool bWatchSubtree, uint dwNotifyFilter, out uint
    lpBytesReturned, IntPtr lpOverlapped,
    ReadDirectoryChangesDelegate lpCompletionRoutine);  

我想开始研究如何创建它,但首先想检查它是否实际上会比标准的托管 c#FileSystemWatcher 执行得更好。

另外(或除此之外),我想知道是否有比以下方法更好的方法让应用程序在后台运行(没有 UI):

while (true)
{
    System.Threading.Thread.Sleep(10000);
}

我觉得这可能不是保持应用程序打开的最佳方式,但我不确定。

有人能给点建议吗?

【问题讨论】:

  • FileSystemWatcher ALREADY uses ReadDirectoryChanges,因此您不太可能通过自己动手来改进。
  • 在 .NET Core 1.0 的开发过程中,微软确实发现了该领域的多个性能问题。您将不得不等到他们将它们登陆 .NET Framework,或者您现在就开发一个 .NET Core 1.0 RTM 应用程序。
  • @MatthewWatson 如果微软确实在这个类中存在错误,这不是 100% 正确的。
  • @LexLi 很有趣 - 你有关于这个的链接吗?
  • @MatthewWatson 我在 GitHub 的糟糕搜索中找不到旧项目,但这个新项目看起来也很有趣,github.com/dotnet/corefx/issues/8059

标签: c# c++ filesystemwatcher


【解决方案1】:

看起来您在 Run 方法中保留了一个线程。不要那样做。而是保留一个静态参考。看起来您正在观看网络共享。不要期望高性能,因为它不仅涉及磁盘子系统,还涉及网络子系统(在您的终端和服务器上)。涉及的因素太多了,无法预期在本机调用相同的 API 会突然带来巨大的性能提升。

static FileSystemWatcher watcher;

private static void Run()
{
    // Create a new FileSystemWatcher and set its properties.
    // if you're watching a network share, don't expect huge performance
    // as the network is involved
    watcher = new FileSystemWatcher
    {
        Path = $@"\\file\home$\{UserName}\Application Data",
        NotifyFilter =  
            NotifyFilters.LastWrite,
        Filter = "filetowatch.txt"
    };

    // Activate
    watcher.Changed += OnChanged;
    watcher.EnableRaisingEvents = true;

    AppDomain.CurrentDomain.DomainUnload += (s,e) => { 
      var w = watcher as IDisposable;
      if (w != null) w.Dispose(); 
    };
}

【讨论】:

  • 从那以后你怎么称呼它?
  • 然后执行 Console.ReadLine()。或者启动一个 Windows 消息泵,但不要像以前那样保持该线程继续运行。我认为这没有帮助。
  • ReadLine 等待硬件中断。它不需要资源。
  • 在 Windows 应用程序中,您有一个消息循环。静态引用保留在内存中,并且消息泵保持活动状态,直到收到 WM_EXIT 或 WM_CLOSE。假设您确实使用 Application.Run 启动了消息泵
  • 它只实现了你不再浪费线程。您可能获得了更少的上下文切换。假设您仍在观看该网络共享,那么您尝试在观察者中挤出的所有内容都是徒劳的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
  • 2019-03-26
  • 2012-03-19
  • 1970-01-01
  • 2014-04-18
  • 2014-01-04
相关资源
最近更新 更多