extensión reactiva rx: ¿cómo hacer que cada suscriptor obtenga un valor diferente (el siguiente) de un observable?

Using reactive extension, it is easy to subscribe 2 times to the same observable. When a new value is available in the observable, both subscribers are called with this same value.

Is there a way to have each subscriber get a different value (the next one) from this observable ?

Ex of what i'm after:
source sequence: [1,2,3,4,5,...] (infinite)
The source is constantly adding new items at an unknown rate.
I'm trying to execute a lenghty async action for each item using N subscribers.

1st subscriber: 1,2,4,...
2nd subscriber: 3,5,...
1st subscriber: 1,3,...
2nd subscriber: 2,4,5,...
1st subscriber: 1,3,5,...
2nd subscriber: 2,4,6,...

preguntado el 01 de febrero de 12 a las 22:02

¿Puedes describir tu guión (i.e. the high level goal you're trying to accomplish?) -

Yes. I want to use a Sql event to grab some new entries from a table and flow them through an action pipeline where actions must be asynchronous and multithreaded (ie: each action may do long work, but can run in // to some extent) -

Is there a solution? -

Yes, Use Microsoft.Tpl.Dataflow and not rx. -

2 Respuestas

I would agree with Asti.

You could use Rx to populate a Queue (Blocking Collection) and then have competing consumers read from the queue. This way if one process was for some reason faster it could pick up the next item potentially before the other consumer if it was still busy.

However, if you want to do it, against good advice :), then you could just use the Select operator that will provide you with the index of each element. You can then pass that down to your subscribers and they can fiter on a modulus. (Yuck! Leaky abstractions, magic numbers, potentially blocking, potentiall side effects to the source sequence etc)

var source = Obserservable.Interval(1.Seconds())
  .Select((i,element)=>{new Index=i, Element=element});

var subscription1 = source.Where(x=>x.Index%2==0).Subscribe(x=>DoWithThing1(x.Element));
var subscription2 = source.Where(x=>x.Index%2==1).Subscribe(x=>DoWithThing2(x.Element));

Also remember that the work done on the OnNext handler if it is blocking will still block the scheduler that it is on. This could affect the speed of your source/producer. Another reason why Asti's answer is a better option.

Ask if that is not clear :-)

Respondido 07 Feb 12, 18:02

Your solution use Polling, which i absolutely want to avoid. But the idea is there. - Softlion

Qué tal si:

IObservable<TRet> SomeLengthyOperation(T input) 
    return Observable.Defer(() => Observable.Start(() => {
        return someCalculatedValueThatTookALongTime;
    }, Scheduler.TaskPoolScheduler));

    .SelectMany(x => SomeLengthyOperation(input))
    .Subscribe(x => Console.WriteLine("The result was {0}", x);

You can even limit the number of concurrent operations:

    .Select(x => SomeLengthyOperation(input))
    .Merge(4 /* at a time */)
    .Subscribe(x => Console.WriteLine("The result was {0}", x);

It's important for the Merge(4) to work, that the Observable returned by SomeLengthyOperation be a Frío Observable, which is what the Defer does here - it makes the Observable.Start not happen until someone Subscribes.

Respondido 08 Feb 12, 01:02

SomeObservableSource must be IObservable<T> and SomeLengthyOperation must return IObservable<TSomething> for the Merge(int) to match - Ana Betts

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.