RxJava Observable.fromEmitter奇数背压行为

时间:2016-10-20 03:56:54

标签: java rx-java

我一直在使用Observable.fromEmitter()作为Observable.create()的绝佳替代品。我最近遇到了一些奇怪的行为,我无法理解为什么会这样。我非常感谢有背压和调度员知识的人。看看这个。

public final class EmitterTest {
  public static void main(String[] args) {
    Observable<Integer> obs = Observable.fromEmitter(emitter -> {
      for (int i = 1; i < 1000; i++) {
        if (i % 5 == 0) {
          sleep(300L);
        }

        emitter.onNext(i);
      }

      emitter.onCompleted();
    }, Emitter.BackpressureMode.LATEST);

    obs.subscribeOn(Schedulers.computation())
        .observeOn(Schedulers.computation())
        .subscribe(value -> System.out.println("Received " + value)); // Why does this get stuck at "Received 128"

    sleep(10000L);
  }

  private static void sleep(Long duration) {
    try {
      Thread.sleep(duration);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }
}

此应用程序的输出是

Received 1
Received 2
...
Received 128

然后它仍然停留在128(假设这是因为这是RxJava的默认缓冲区大小)。

如果我将fromEmitter()中指定的模式更改为BackpressureMode.NONE,则代码按预期工作。如果我删除了对observeOn()的调用,它也会按预期工作。有人能够解释为什么会这样吗?

2 个答案:

答案 0 :(得分:2)

这是同一个池的死锁情况。 subscribeOn在它正在使用的同一个线程上调度下游request,但如果该线程忙于睡眠/发射循环,则请求永远不会传递给fromEmitter,因此在一段时间之后{如果主源等待足够长的话,{1}}开始将元素向上移动直到最后一个值(999)被传递到最后。 (这与我们删除LATEST的情况类似。)

如果onBackpressureBlock未执行此请求计划,则该示例将起作用。

我已经打开an issue来制定解决方案。

现在的解决方法是使用更大的缓冲区大小subscribeOn(有重载)或使用observeOn

答案 1 :(得分:1)

这并不奇怪,这是预期的。

让我们跟踪电话。从:

开始

Observable.subscribe(Subscriber<? super T> subscriber)

Observable.subscribe(Subscriber<? super T> subscriber, Observable<T> observable)

RxJavaHooks.onObservableStart(observable, observable.onSubscribe).call(subscriber);

Subscriber<? super T> st = RxJavaHooks.onObservableLift(operator).call(o);

等等。查看以下构造函数:

OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize)

public OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize) {
    this.scheduler = scheduler;
    this.delayError = delayError;
    this.bufferSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE;
}

如果您未指定缓冲区,则默认值为RxRingBuffer.SIZE,其大小取决于平台。

这就是为什么当你调用没有缓冲区大小的observeOn运算符时,默认值为128(Android上为16)。

此问题的解决方案非常简单:只需使用另一个observeOn运算符并声明缓冲区大小。但是如果你声明缓冲区大小为1000(与来自发射器的元素一样多),程序仍将结束而不会发出所有值(大约170)。为什么?因为程序结束了。主线程在10 000秒后结束,您在另一个线程(Schedulers.computation())中完成计算。解决方案呢?使用CountdownLatch。请注意,永远不要使用它来生产,只是为了测试它才是安全的。