14

C#でFileSystemWatcherのエラーを回避するには?

ディレクトリで一度に変更が多すぎる

ネットワーク共有上のすべての変更を検出する必要があります。InternalBufferSize が 8192 * 128 に増加

4

6 に答える 6

22

次の 2 つのことを行う必要があります。

  1. InternalBufferSizeサポートされている最大値 (65536) に設定します。「8192 * 128」に設定しようとすると、ドキュメントに記載されているサポートされている最大値よりも大きいため、バッファ サイズをまったく増やしていない可能性があります。
  2. 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 アップストリームはおそらく低速です)、ここにはまだ最適化の余地があります。ただし、ディスパッチャーが「転換点」を超えている限り、送信者がイベントをディスパッチするよりも速くポストできない場合、バックログが発生することはなく、したがってバッファーが吹き飛ばされることもありません。このアプローチは、あなたをその安全な領域に導くと信じています。

于 2016-02-16T12:01:29.087 に答える
4

からMSDN;

Windowsオペレーティングシステムは、によって作成されたバッファ内のファイルの変更をコンポーネントに通知しますFileSystemWatcher。短時間に多くの変更がある場合、バッファがオーバーフローする可能性があります。これにより、コンポーネントはディレクトリ内の変更を追跡できなくなり、包括的な通知のみが提供されます。プロパティを使用してバッファのサイズを増やすと、InternalBufferSizeディスクにスワップアウトできない非ページメモリから発生するため、コストがかかります。したがって、ファイル変更イベントを見逃さないように、バッファを小さく、しかも十分に大きくしてください。バッファオーバーフローを回避するには、NotifyFilterおよび IncludeSubdirectoriesプロパティを使用して、不要な変更通知を除外できるようにします。

于 2013-03-20T09:05:13.440 に答える
2

FileSystemWatcher が常に最も信頼できるものであるとは限らないという経験があります。フィルターを指定して、監視しているファイルを絞り込む (NotifyFilter) か、バッファー サイズを増やすことができます。

ただし、要件によっては、x 秒ごとにポーリングしてファイルのリストを取得するなど、別の方法を使用することもできます。ただし、ビジネス ケースについて詳しく説明する必要がある場合があります。

于 2013-03-20T09:03:16.073 に答える
0

SHChangeNotifyRegister を使用して、シェル通知を取得できます。

于 2013-03-20T11:44:50.207 に答える