Implementación de cola Rx y búfer de Dispatcher
Frecuentes
Visto 1,752 equipos
3
Quiero implementar una cola que sea capaz de tomar eventos/elementos de múltiples productores en múltiples subprocesos y consumirlos todos en un solo subproceso. esta cola funcionará en algún entorno crítico, por lo que estoy bastante preocupado por su estabilidad.
Lo he implementado usando las capacidades de Rx, pero tengo 2 preguntas:
- ¿Está bien esta implementación? ¿O tal vez tiene algún defecto que desconozco? (como alternativa - implementación manual con cola y bloqueos)
- ¿Cuál es la longitud del búfer de Dispatcher? ¿Puede manejar 100k de elementos en cola?
El siguiente código ilustra mi enfoque, usando un TestMethod simple. Su salida muestra que todos los valores se ingresan desde diferentes subprocesos, pero se procesan en otro subproceso único.
[TestMethod()]
public void RxTest()
{
Subject<string> queue = new Subject<string>();
queue
.ObserveOnDispatcher()
.Subscribe(s =>
{
Debug.WriteLine("Value: {0}, Observed on ThreadId: {1}", s, Thread.CurrentThread.ManagedThreadId);
},
() => Dispatcher.CurrentDispatcher.InvokeShutdown());
for (int j = 0; j < 10; j++)
{
ThreadPool.QueueUserWorkItem(o =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
queue.OnNext(string.Format("value: {0}, from thread: {1}", i.ToString(), Thread.CurrentThread.ManagedThreadId));
}
queue.OnCompleted();
});
}
Dispatcher.Run();
}
2 Respuestas
4
No estoy seguro del comportamiento de Subject
en escenarios con múltiples subprocesos. Sin embargo, me puedo imaginar que algo como BlockingCollection
(y su subyacente ConcurrentQueue
) están bien usados en las situaciones de las que estás hablando. Y fácil de arrancar.
var queue = new BlockingCollection<long>();
// subscribing
queue.GetConsumingEnumerable()
.ToObservable(Scheduler.NewThread)
.Subscribe(i => Debug.WriteLine("Value: {0}, Observed on ThreadId: {1}", i, Thread.CurrentThread.ManagedThreadId));
// sending
Observable.Interval(TimeSpan.FromMilliseconds(500), Scheduler.ThreadPool)
.Do(i => Debug.WriteLine("Value: {0}, Sent on ThreadId: {1}", i, Thread.CurrentThread.ManagedThreadId))
.Subscribe(i => queue.Add(i));
Ciertamente no querrás tocar colas y bloqueos. El ConcurrentQueue
la implementación es excelente y ciertamente manejará las colas de tamaño de las que está hablando de manera efectiva.
Respondido 02 Jul 12, 12:07
3
Eche un vistazo a EventLoopScheduler
. Está integrado en RX y creo que hace todo lo que quieres.
Puede tomar cualquier número de observables, llame .ObserveOn(els)
(els
es tu instancia de un EventLoopScheduler
) y ahora está organizando múltiples observables de múltiples subprocesos en un solo subproceso y poniendo en cola cada llamada para OnNext
en serie.
Respondido 02 Jul 12, 12:07
EventLoopScheduler podría ser mi elección, pero ¿es posible "ejecutar" ese programador en el hilo actual? ¿En la forma de bloqueo de Dispatcher's Run? - oveja sorda
@deafsheep EventLoopScheduler ejecuta en serie el trabajo en diferentes subprocesos para cada elemento. El CurrentThreadScheduler ejecuta código en el subproceso actual (que es automáticamente en serie porque un subproceso solo puede hacer una cosa a la vez). También genera situaciones de interbloqueo más rápido de lo que puede escribir si lo usa mal. Para la situación que describió, Vanilla EventLoopScheduler es probablemente el camino a seguir. - anderson imes
@AndersonImes - El EventLoopScheduler
solo usa un hilo nuevo si no hay elementos pendientes. Si hay un número en cola, continúa usando el mismo hilo. - Enigmatividad
@Enigmativity ah, tienes razón, esa es una distinción importante. Parece que hace un EnsureThread()
en una nueva llamada programada y una this.thread = null
cuando la cola está vacía. De todos modos, la tuya sigue siendo probablemente la recomendación correcta. :) - anderson imes
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas system.reactive or haz tu propia pregunta.
Muchas gracias, su ejemplo es muy bueno y funciona exactamente como lo necesito sin tener que jugar con Dispatcher. - oveja sorda
Aunque, creo que hay un problema en su implementación: ¿qué pasa si esta cola funciona durante un mes en vivo? Almacenará todos los artículos en él, ¿verdad? ¿Significa que consumirá multitudes en la memoria para contener todo eso? - oveja sorda
Los elementos de @deafsheep se están agotando de la cola, por lo que no los almacenará todos a menos que no se consuman, que es el comportamiento que desea. - yamen
@yamen, por lo que si tuviera que agregar otro consumidor, tendría que obtener otro enumerable de consumo y cambiarlo a observable, ¿verdad? ¿Y para eliminar al consumidor solo dispongo de la suscripción? - Krzysztof Skowronek