.net system.reactive

.net - Extensiones reactivas vs FileSystemWatcher



system.reactive (4)

BufferWithTime.Where (). Select (...) hará el trabajo, pero lo que realmente desea es Throttle()

Una de las cosas que me molestó durante mucho tiempo acerca de FileSystemWatcher es la forma en que dispara varios eventos para un solo cambio lógico en un archivo. Sé por qué sucede, pero no quiero tener que preocuparme, solo quiero volver a probar el archivo una vez, no 4-6 veces seguidas. Idealmente, habría un evento que solo se dispara cuando un archivo dado termina de cambiar, en lugar de cada paso en el camino.

A lo largo de los años he encontrado varias soluciones a este problema, de diversos grados de fealdad. Pensé que las Extensiones reactivas serían la solución definitiva, pero hay algo que no estoy haciendo bien y espero que alguien pueda señalar mi error.

Tengo un método de extensión:

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

En última instancia, me gustaría obtener un evento por nombre de archivo, dentro de un período de tiempo determinado, de modo que cuatro eventos seguidos con un solo nombre de archivo se reduzcan a un evento, pero no pierdo nada si se modifican varios archivos al mismo tiempo. hora. BufferWithTime suena como la solución ideal.

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

Cuando me suscribo a este observable, un solo cambio en un archivo supervisado dispara mi método de suscripción cuatro veces seguidas, lo que anula el propósito. Si elimino la llamada Distinct() , veo que cada una de las cuatro llamadas contiene dos eventos idénticos, por lo que se está produciendo un búfer. Aumentar el TimeSpan pasado a BufferWithTime parece no tener ningún efecto; fui tan alto como 20 segundos sin ningún cambio en el comportamiento.

Esta es mi primera incursión en Rx, así que probablemente me esté perdiendo algo obvio. ¿Lo estoy haciendo mal? ¿Hay un mejor enfoque? Gracias por cualquier sugerencia ...


Mi error. De alguna manera tengo varios FileSystemWatchers monitoreando las carpetas de cada uno. El observable se activaba una vez para cada observador, pero BufferWithTime parece estar funcionando correctamente. Todavía tengo que averiguar por qué mis observadores están activando eventos para carpetas que pensé que estaban configurados para ignorar, pero eso no tiene nada que ver con Rx o esta pregunta.

De hecho, tal vez pueda solucionar ese problema y cambiar a tener un solo observador monitoreando una carpeta principal, usando Rx para filtrar los eventos de las carpetas que no me interesan.


Solo para calentar un viejo tema, ya que estoy trabajando en eso en este momento también:

Por supuesto, este tema es insignificante en el contexto de ver un archivo, ya que FileSystemWatcher solo se dispara cada ~ 3 segundos con un evento de Cambio para un solo archivo cuando realiza un seguimiento del Tamaño a través de

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

Pero asumamos que FileSystemWatcher dispararía muchos eventos seguidos (quizás muchos archivos se cambian / renombran / crean), y otras personas leen esto:

No desea utilizar Throttle o BufferWithTime en este caso: Throttle es un poco confuso ... prohíbe cualquier disparo hasta que transcurra el tiempo de TimeSpan sin un evento. Significado: nunca podría dispararse cuando usas algo como Throttle(TimeSpan.FromMilliseconds(200)) , y después de cada evento hay una pausa <200 ms. Así que en realidad no es lo que la "regulación" espera la gente. Es bueno para la entrada del usuario, cuando desea esperar hasta que el usuario haya dejado de escribir algo. Es malo para la regulación de la carga.

BufferWithTime tampoco es lo que quieres: solo llena un timebuffer. Es bueno cuando tiene una alta carga inicial por evento, como abrir una conexión a un servicio web. En ese caso, desearía procesar por lotes los eventos cada "vez" segundos. Pero no cuando se carga la carga, ya que la cantidad de eventos no cambia.

La solución es el método Sample(TimeSpan time) : toma el último evento dentro de un TimeSpan, que es el acelerador "real". Creo que los chicos de Rx realmente arruinaron el nombramiento en este caso.


podría usar un grupo por para agregar eventos del sistema de archivos por nombre de archivo, y usar el observable resultante con el método de extensiones Throttle. He escrito una pequeña muestra usando números enteros, pero la idea básica es la misma.

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));

salidas:

9 19 29 39 40

que es para cada grupo ( n/10 ) el último entero observado.