次の同期の問題があります。
スレッドはループで実行され、まだ処理されていないファイルシステム内のフォルダー (アプリが実行されていない間に追加された可能性があります) が検出された場合、そのフォルダーは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 によって行われた変更が常にループによって考慮されるようにする方法は?