我一直在使用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()
的调用,它也会按预期工作。有人能够解释为什么会这样吗?
答案 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
。请注意,永远不要使用它来生产,只是为了测试它才是安全的。