我有以下同步问题。
线程循环运行,如果它检测到文件系统中尚未处理的文件夹(可能在应用程序未运行时已添加),则该文件夹将通过以下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
而不是作为新的处理。
但有时会出现以下顺序:
- 在 Windows 资源管理器和文件系统中重命名文件夹(当循环保持锁定时)
- fsw_Renamed 发生,但无法获取锁
- 循环线程仍保持锁定并将文件夹拾取为新文件夹(尚未标记为重命名)
- 循环线程释放锁
- 发生重命名事件,获得锁,文件夹被标记为重命名。但是已经为时已晚,因为它已经作为新的处理了。
我无法弄清楚如何确保重命名的文件夹永远不会作为新文件夹处理。
重命名的文件夹是Renamed
事件发生或将在不久的将来发生的文件夹,因为文件系统更改和事件之间存在固有的延迟FileSystemWatcher.Renamed
(文件系统的实际更改和引发的事件不是原子的)。
如何确保循环始终考虑 fsw_Renamed 所做的更改,即使事件发生在循环持有锁时?