1

我有以下同步问题。

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

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

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

为了解决这个问题,我有一个事件FileSystemWatcher处理程序Renamed

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 资源管理器和文件系统中重命名文件夹(当循环保持锁定时)
  2. fsw_Renamed 发生,但无法获取锁
  3. 循环线程仍保持锁定并将文件夹拾取为新文件夹(尚未标记为重命名)
  4. 循环线程释放锁
  5. 发生重命名事件,获得锁,文件夹被标记为重命名。但是已经为时已晚,因为它已经作为新的处理了。

我无法弄清楚如何确保重命名的文件夹永远不会作为新文件夹处理。

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

如何确保循环始终考虑 fsw_Renamed 所做的更改,即使事件发生在循环持有锁时?

4

2 回答 2

1

您要处理文件夹,根据文件夹是新文件夹还是简单重命名文件夹,有两种不同的处理方式。您正在使用 fs 监视对象,该对象在给定文件夹已重命名时触发事件。您应该对新文件夹使用类似的监控,并让这些事件创建绑定到该文件夹​​的任务。

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

当一个文件夹被安排处理时,发生在它们身上的任何事件都应该被监视器过滤掉,通过检查任务队列来查看文件夹是否存在。它甚至可以根据其他因素更改任务或将其删除(例如,如果文件夹在处理之前已被删除 - 也许监视该事件也会很好)。

一个任务可能是这样的:

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

您不能保留 renameLock,因为它首先是造成问题的原因。如果您无法锁定文件夹,也许您可​​以将其移动(我想这在技术上类似于重命名)以进行处理,然后将其移回原位。

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

于 2012-11-21T13:39:21.090 回答
1

您可以尝试以下方法:

  1. 在应用程序启动时运行该Loop方法。可能在订阅.FileSystemWatcher
  2. 在您订阅FileSystemWatcher.Created的目录上订阅FileSystemWatcher.Renamed
  3. 保持FileSystemWatcher.Renamed订阅不变。
  4. 移除锁,因为这两个事件不应相互冲突。

第 1 步将处理在您的应用程序未运行时创建/重命名的目录。

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

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

于 2012-11-21T16:05:06.403 回答