如何同步filesystemwatcher事件的处理?

时间:2012-11-21 12:10:03

标签: c# .net multithreading locking filesystemwatcher

我遇到以下同步问题。

一个线程在一个循环中运行,如果它检测到文件系统中尚未处理的文件夹(它可能是在应用程序未运行时添加的),则Process将该文件夹作为新文件夹处理方法:

void Loop()
{
 while (run)
 {
  lock(renameLock)
  {
    var newFolders = EnumerateNewFolders().ExceptThoseMarkedAsRenamed();
    Process(newFolders);
    MarkAsProcessed(newFolders);
  }
  Sleep();
 }
}

重命名的文件夹不应该作为新文件处理,而是作为重命名处理并标记为重命名,以便以后不再处理它们。

为了解决这个问题,我有一个FileSystemWatcherRenamed事件的处理程序:

private void fsw_Renamed(object sender, RenamedEventArgs e)
{
  lock(renameLock)
  {
    ProcessRename(e);
    //mark folder as renamed so that ExceptThoseMarkedAsRenamed filters it out
    MarkAsRenamed(e);
  }
}

通常这样做,并且在执行重命名时,该文件夹首先在fsw_Renamed中标记为重命名,然后在ExceptThoseMarkedAsRenamed循环过滤掉,而不是新处理。

但有时会出现以下顺序:

  1. 在Windows资源管理器和文件系统中重命名文件夹(当Loop保持锁定时)
  2. fsw_Renamed发生,但无法获取锁
  3. 循环线程仍然保持锁定并将文件夹作为新文件夹(它未标记为已重命名)
  4. 循环线程释放锁
  5. 重命名事件发生,获取锁定,文件夹被标记为重命名。但它已经太晚了,因为它已经被处理为新的。
  6. 我无法确定如何确保重命名的文件夹永远不会被处理为新文件夹。

    重命名的文件夹是Renamed事件发生或将在近期发生的文件夹,因为文件系统更改与FileSystemWatcher.Renamed事件之间存在固有延迟(并且文件系统和引发的事件的实际更改不是原子的。)

    如何确保循环始终考虑fsw_Renamed完成的更改,即使Loop在持锁时发生了事件?

2 个答案:

答案 0 :(得分:1)

您希望根据文件夹是新文件夹还是仅重命名文件来处理文件夹,并进行两种不同的处理。 您正在使用fs监视对象,该对象在重命名给定文件夹时触发事件。 您应该对新文件夹使用类似的监视,并让这些事件创建绑定到该文件夹​​的任务。

捕获事件的线程不会出现竞争条件问题,并且只会调度新任务,因此不会因处理而延迟,并且能够在事件发生时处理新事件。 任务队列可以只是包含文件夹和要应用于其的任务的对列表。

当计划文件夹进行处理时,监视器应过滤掉发生在它们上的任何事件,方法是检查任务队列以查看该文件夹是否存在。它甚至可以根据其他因素更改任务或删除它(如果文件夹在被处理之前已被删除 - 也许监视该事件也是好的。)

任务可能类似于:

  • 锁定要处理的文件夹(这样可以避免对文件夹产生外部影响)
  • 处理它
  • 将其标记为"已处理" (把它从列表中删除)
  • 解锁

你不能保留renameLock,因为它首先是创建问题的东西。 如果你无法锁定文件夹,也许你可以在其他地方移动它(它在技术上就像重命名一样)进行处理,然后将其移回原位。

SO question处理在C#中锁定文件夹的问题。

答案 1 :(得分:1)

您可以尝试以下方法:

  1. 在应用启动时运行Loop方法 。可能在订阅FileSystemWatcher之前。
  2. 订阅您订阅FileSystemWatcher.Created目录的FileSystemWatcher.Renamed
  3. 保持FileSystemWatcher.Renamed订阅原样。
  4. 删除锁定,因为这两个事件不应相互冲突。
  5. 步骤1.将处理在您的应用程序运行时创建/重命名的目录。

    步骤2.将在您的应用程序 正在运行时处理新目录。

    步骤3.将在您的应用程序 正在运行时处理重命名的目录。