C#でFileSystemWatcherのエラーを回避するには?
ディレクトリで一度に変更が多すぎる
ネットワーク共有上のすべての変更を検出する必要があります。InternalBufferSize が 8192 * 128 に増加
C#でFileSystemWatcherのエラーを回避するには?
ディレクトリで一度に変更が多すぎる
ネットワーク共有上のすべての変更を検出する必要があります。InternalBufferSize が 8192 * 128 に増加
次の 2 つのことを行う必要があります。
InternalBufferSize
サポートされている最大値 (65536) に設定します。「8192 * 128」に設定しようとすると、ドキュメントに記載されているサポートされている最大値よりも大きいため、バッファ サイズをまったく増やしていない可能性があります。FileSystemWatcher
からのイベントを処理のためにバックグラウンド スレッドのキューに入れます。これはよく理解されていない 2 番目のポイントであり、MSDN で文書化する必要があります。内部的にFileSystemWatcher
は、上記のサイズを設定した内部バッファーに変更イベントをキューに入れています。ただし、重要なのは、イベント ハンドラーが を返した後にのみ、アイテムがそのバッファーから削除されることです。これは、イベント ハンドラーが導入するオーバーヘッドのサイクルごとに、バッファーがいっぱいになる可能性が高くなることを意味します。あなたがしなければならないことはFileSystemWatcher
、可能な限り迅速に制限されたキューをクリアし、イベントを独自の無限キューに移動して、処理できる速度で処理するか、そうしたい場合は破棄しますが、その周りにある程度の知性が必要です.
基本的に、コードで行うことは次のとおりです。まず、独自のディスパッチャ スレッドを開始します。
Dispatcher changeDispatcher = null;
ManualResetEvent changeDispatcherStarted = new ManualResetEvent(false);
Action changeThreadHandler = () =>
{
changeDispatcher = Dispatcher.CurrentDispatcher;
changeDispatcherStarted.Set();
Dispatcher.Run();
};
new Thread(() => changeThreadHandler()) { IsBackground = true }.Start();
changeDispatcherStarted.WaitOne();
次に、ウォッチャーを作成します。設定されているバッファ サイズに注意してください。私の場合、サブディレクトリではなく、ターゲット ディレクトリの変更のみを監視します。
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.InternalBufferSize = 64 * 1024;
watcher.IncludeSubdirectories = false;
ここでイベント ハンドラーをアタッチしますが、ウォッチャー スレッドで同期的に実行するのではなく、ディスパッチャーで呼び出します。はい、イベントはディスパッチャーによって順番に処理されます。
watcher.Changed += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnChanged(sender, e)));
watcher.Created += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnCreated(sender, e)));
watcher.Deleted += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnDeleted(sender, e)));
watcher.Renamed += (sender, e) => changeDispatcher.BeginInvoke(new Action(() => OnRenamed(sender, e)));
そして最後に、を破棄した後FileSystemWatcher
(そうしていましたよね?)、ディスパッチャーをシャットダウンする必要があります。
watcher.Dispose()
changeDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
以上です。ネットワークとローカルの両方のシナリオで、私はこの問題を自分で抱えていました。このアプローチを使用した後、空のファイルをできるだけ早く監視ディレクトリに打ち込んでも、このエラーを再び生成することはできませんでした。この場合、どうにかしてバッファを使い果たすことができた場合 (これが可能かどうかはわかりませんが、API アップストリームはおそらく低速です)、ここにはまだ最適化の余地があります。ただし、ディスパッチャーが「転換点」を超えている限り、送信者がイベントをディスパッチするよりも速くポストできない場合、バックログが発生することはなく、したがってバッファーが吹き飛ばされることもありません。このアプローチは、あなたをその安全な領域に導くと信じています。
からMSDN
;
Windowsオペレーティングシステムは、によって作成されたバッファ内のファイルの変更をコンポーネントに通知します
FileSystemWatcher
。短時間に多くの変更がある場合、バッファがオーバーフローする可能性があります。これにより、コンポーネントはディレクトリ内の変更を追跡できなくなり、包括的な通知のみが提供されます。プロパティを使用してバッファのサイズを増やすと、InternalBufferSize
ディスクにスワップアウトできない非ページメモリから発生するため、コストがかかります。したがって、ファイル変更イベントを見逃さないように、バッファを小さく、しかも十分に大きくしてください。バッファオーバーフローを回避するには、NotifyFilter
およびIncludeSubdirectories
プロパティを使用して、不要な変更通知を除外できるようにします。
FileSystemWatcher が常に最も信頼できるものであるとは限らないという経験があります。フィルターを指定して、監視しているファイルを絞り込む (NotifyFilter) か、バッファー サイズを増やすことができます。
ただし、要件によっては、x 秒ごとにポーリングしてファイルのリストを取得するなど、別の方法を使用することもできます。ただし、ビジネス ケースについて詳しく説明する必要がある場合があります。
SHChangeNotifyRegister を使用して、シェル通知を取得できます。