19

FileSystemWatcher について長い間悩まされてきたことの 1 つは、ファイルに対する単一の論理変更に対して複数のイベントを発生させる方法です。理由はわかっていますが、気にする必要はありません。ファイルを 4 ~ 6 回続けて再解析するのではなく、1 回だけ再解析したいだけです。理想的には、途中のすべてのステップではなく、特定のファイルの変更が完了したときにのみ発生するイベントがあります。

何年にもわたって、私はこの問題に対してさまざまな程度の醜さのさまざまな解決策を考え出しました。Reactive Extensions が究極のソリューションになると思っていましたが、私が正しく行っていないことがあり、誰かが私の間違いを指摘してくれることを願っています。

私は拡張メソッドを持っています:

public static IObservable<IEvent<FileSystemEventArgs>> GetChanged(this FileSystemWatcher that)
{
    return Observable.FromEvent<FileSystemEventArgs>(that, "Changed");
}

最終的に、特定の期間内にファイル名ごとに 1 つのイベントを取得したいと考えています。これにより、単一のファイル名を持つ連続した 4 つのイベントが 1 つのイベントに削減されますが、複数のファイルが同時に変更されても何も失われません。時間。BufferWithTime理想的なソリューションのように聞こえます。

var bufferedChange = watcher.GetChanged()
    .Select(e => e.EventArgs.FullPath)
    .BufferWithTime(TimeSpan.FromSeconds(1))
    .Where(e => e.Count > 0)
    .Select(e => e.Distinct());

このオブザーバブルをサブスクライブすると、監視対象のファイルを 1 回変更すると、サブスクリプション メソッドが 4 回連続してトリガーされ、目的が果たせなくなります。呼び出しを削除するDistinct()と、4 つの呼び出しのそれぞれに 2 つの同一のイベントが含まれていることがわかります。そのため、バッファリングが行われています。渡された TimeSpan を増やしBufferWithTimeても効果がないようです。動作を変更せずに 20 秒まで上げました。

これは Rx への私の最初の進出なので、おそらく明らかな何かが欠けています。私はそれを間違っていますか?より良いアプローチはありますか?提案をありがとう...

4

4 に答える 4

12

私も現在それに取り組んでいるので、古いトピックをウォームアップするためだけに:

もちろん、このトピックは、1 つのファイルを監視するコンテキストでは無視できます。FileSystemWatcher は、Size を介してサイズを追跡するときに、1 つのファイルの Changed イベントで ~3 秒ごとにしか起動しないためです。

_fileSystemWatcher.NotifyFilter = NotifyFilters.Size | ....

しかし、FileSystemWatcher が連続して多くのイベントを発生させ (多くのファイルが変更/名前変更/作成される可能性があります)、他の人がこれを読んだと仮定しましょう:

この場合、Throttle または BufferWithTime を使用したくありません Throttle。これは少し誤解を招く可能性があります。イベントなしで TimeSpan 時間が経過するまで、発火を禁止します。意味: のようなものを使用すると、決して起動できずThrottle(TimeSpan.FromMilliseconds(200))、すべてのイベントの後に 200 ミリ秒未満の一時停止があります。したがって、人々が期待する「スロットリング」ではありません。ユーザーが何かを入力するのをやめるまで待ちたいときのユーザー入力に適しています。ロードスロットリングには悪いです。

BufferWithTimeまた、あなたが望むものではありません:タイムバッファを埋めるだけです。Web サービスへの接続を開くなど、イベントごとの初期負荷が高い場合に適しています。その場合、「time」秒ごとにイベントをバッチ処理する必要があります。ただし、イベントの数は変わらないため、負荷分散の場合はそうではありません。

解決策はSample(TimeSpan time)メソッドです。「実際の」スロットルである TimeSpan 内の最後のイベントを取得します。この場合、Rxの連中はネーミングを本当に台無しにしたと思います。

于 2012-01-09T09:42:07.430 に答える
5

group byを使用して、ファイル名ごとにファイルシステムイベントを集約し、結果のobservableをThrottleextensionsメソッドで使用できます。整数を使用して小さなサンプルを作成しましたが、基本的な考え方は同じです。

var obs = from n in Enumerable.Range(1, 40).ToObservable()
    group n by n / 10 into g
    select new { g.Key, Obs = g.Throttle(TimeSpan.FromMilliseconds(10.0)) } into h
    from x in h.Obs
    select x;
obs.Subscribe(x => Console.WriteLine(x));

出力:

9 
19 
29 
39 
40 

これは、グループごとに(n/10)最後に観測された整数です。

于 2012-10-12T06:53:35.237 に答える
3

私の間違い。どういうわけか、私は複数のFileSystemWatchersがお互いのフォルダを監視しています。オブザーバブルはウォッチャーごとに1回トリガーされていましたBufferWithTimeが、正しく機能しているようです。ウォッチャーが無視するように構成されていると思ったフォルダーに対してイベントを発生させる理由を理解する必要がありますが、それはRxやこの質問とは何の関係もありません。

実際、私はその問題をパントして、親フォルダーを監視する単一のウォッチャーに切り替えることができます。Rxを使用して、関心のないフォルダーからイベントをフィルターで除外します。

于 2010-04-20T21:36:42.210 に答える
3

BufferWithTime.Where().Select(...) は仕事をしますが、本当に欲しいのはThrottle()

于 2010-04-22T00:12:32.100 に答える