Reactive pipeline - how to control parallelism?

后端 未结 2 1278
时光说笑
时光说笑 2020-12-21 01:52

I\'m building a straightforward processing pipeline where an item is fetched as an input, it is being operated by multiple processors in a sequential manner and finally it i

2条回答
  •  無奈伤痛
    2020-12-21 02:18

    Merge provides an overload which takes a max concurrency.

    Its signature looks like: IObservable Merge(this IObservable> source, int maxConcurrency);

    Here is what it would look like with your example (I refactored some of the other code as well, which you can take or leave):

    return Observable
    //Reactive while loop also takes care of the onComplete for you
    .While(() => _provider.HasNext, 
           Observable.FromAsync(_provider.GetNextAsync))
    //Makes return items that will only execute after subscription
    .Select(item => Observable.Defer(() => {
      return _processers.Aggregate(
        seed: Observable.Return(item),
        func: (current, processor) => current.SelectMany(processor.ProcessAsync)); 
      }))
     //Only allow 3 streams to be execute in parallel.
    .Merge(3);
    

    To break down what this does,

    1. While will check each iteration, if _provider.HasNext is true, if so then it will resubscribe to get the next value for _provider, otherwise it emits onCompleted
    2. Inside of select a new observable stream is created, but not yet evaluated by using Defer
    3. The returned IObservable> is passed to Merge which subscribes to a max of 3 observables simultaneously.
    4. The inner observable finally evaluates when it is subscribed to.

    Alternative 1

    If you also need to control the number of parallel requests you need to get a little trickier, since you will need to signal that your Observable is ready for new values:

    return Observable.Create(observer => 
    {
      var subject = new Subject();
      var disposable = new CompositeDisposable(subject);
    
      disposable.Add(subject
        //This will complete when provider has run out of values
        .TakeWhile(_ => _provider.HasNext)
        .SelectMany(
          _ => _provider.GetNextAsync(),
         (_, item) => 
         {
           return _processors
            .Aggregate(
             seed: Observable.Return(item),
             func: (current, processor) => current.SelectMany(processor.ProcessAsync))
            //Could also use `Finally` here, this signals the chain
            //to start on the next item.
            .Do(dontCare => {}, () => subject.OnNext(Unit.Default));
         }
        )
        .Merge(3)
        .Subscribe(observer));
    
      //Queue up 3 requests for the initial kickoff
      disposable.Add(Observable.Repeat(Unit.Default, 3).Subscribe(subject.OnNext));
    
      return disposable;
    });
    

提交回复
热议问题