19

FileSystemWatcher 长期以来一直困扰着我的一件事是它为文件的单个逻辑更改触发多个事件的方式。我知道它为什么会发生,但我不想关心 - 我只想重新解析文件一次,而不是连续 4-6 次。理想情况下,只有在给定文件完成更改时才会触发一个事件,而不是沿途的每一步。

多年来,我为这个问题提出了各种不同程度的丑陋解决方案。我认为 Reactive Extensions 将是最终的解决方案,但有些事情我做得不对,我希望有人能指出我的错误。

我有一个扩展方法:

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

最终,我想在给定的时间段内为每个文件名获取一个事件 - 这样一来,具有单个文件名的连续四个事件减少为一个事件,但如果同时修改多个文件,我不会丢失任何内容时间。BufferWithTime听起来像是理想的解决方案。

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

当我订阅这个 observable 时,对受监视文件的一次更改会连续四次触发我的订阅方法,这完全违背了目的。如果我删除该Distinct()调用,我会看到四个调用中的每一个都包含两个相同的事件 - 因此正在进行一些缓冲。增加传递给的 TimeSpanBufferWithTime似乎没有任何效果 - 我高达 20 秒,但行为没有任何变化。

这是我第一次涉足 Rx,所以我可能遗漏了一些明显的东西。我做错了吗?有更好的方法吗?感谢您的任何建议...

4

4 回答 4

12

只是为了热身一个老话题,因为我现在也在做这个:

当然,在观看一个文件的情况下,这个主题可以忽略不计,因为当您通过

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

但是让我们假设 FileSystemWatcher 会连续触发许多事件(可能许多文件被更改/重命名/创建),其他人读到这个:

在这种情况下,您不想使用 Throttle 或 BufferWithTime : Throttle有点误导......它禁止任何触发,直到 TimeSpan 时间过去而没有事件。含义:当你使用类似的东西时它永远不会触发Throttle(TimeSpan.FromMilliseconds(200)),并且在每个事件之后都有一个 < 200 ms 的暂停。所以这并不是人们所期望的“节流”。当您想等到用户停止输入某些内容时,这对用户输入很有用。这对负载节流不利。

BufferWithTime也不是您想要的:它只是填充时间缓冲区。当您每个事件的初始负载很高时很好,例如打开到 Web 服务的连接。在这种情况下,您可能希望每隔“时间”秒批处理一次事件。但不是在负载平衡时,因为事件的数量不会改变。

解决方案是Sample(TimeSpan time)方法:它采用 TimeSpan 内的最后一个事件,即“真正的”Throttle。我认为在这种情况下,Rx 的人真的搞砸了命名。

于 2012-01-09T09:42:07.430 回答
5

您可以使用 group by 来聚合每个文件名的文件系统事件,并将生成的 observable 与 Throttle 扩展方法一起使用。我用整数写了一个小样本,但基本思想是一样的。

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 监视彼此的文件夹。观察者为每个观察者触发一次,但BufferWithTime似乎工作正常。我仍然需要弄清楚为什么我的观察者会为我认为它们被配置为忽略的文件夹触发事件,但这与 Rx 或这个问题无关。

事实上,也许我可以解决这个问题,并切换到让单个观察者监视父文件夹,使用 Rx 过滤掉我不感兴趣的文件夹中的事件。

于 2010-04-20T21:36:42.210 回答
3

BufferWithTime.Where().Select(...) 将完成这项工作,但你真正想要的是Throttle()

于 2010-04-22T00:12:32.100 回答