为什么RxJava zip操作符在最后发出值的线程上工作?

时间:2017-06-16 10:11:33

标签: android multithreading rx-java

我试图压缩两个在不同线程上发射的Observable:

Observable<String> xxxx1 = Observable.fromCallable((Func0<String>) () -> {
    try {
        Thread.sleep((long)(Math.random() * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "First";
})
        .doOnNext(s -> Log.d("TEEEST", "1 onNext " + s + " thread " + Thread.currentThread().getName()))
        .subscribeOn(Schedulers.computation());

Observable<String> xxxx2 = Observable.fromCallable((Func0<String>) () -> {
    try {
        Thread.sleep((long)(Math.random() * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Second";
})
        .doOnNext(s -> Log.d("TEEEST", "2 onNext " + s + " thread " + Thread.currentThread().getName()))
        .subscribeOn(Schedulers.io());

Observable.zip(xxxx1, xxxx2, (s1, s2) -> {
    Log.d("TEEEST", "zip func thread " + Thread.currentThread().getName());
    return s1.concat(s2);
})
        .map(s -> {
            Log.d("TEEEST", "map thread " + Thread.currentThread().getName());
            return s.concat(" mapped");
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(s -> {
            Log.d("TEEEST", "call " + s + " thread " + Thread.currentThread().getName());
        });

似乎zip在作为最后一个发出值的线程上工作,这是我的日志的样子:

首先运行:

D/TEEEST: 2 onNext Second thread RxIoScheduler-3
D/TEEEST: 1 onNext First thread RxComputationScheduler-1
D/TEEEST: zip func thread RxComputationScheduler-1
D/TEEEST: map thread RxComputationScheduler-1
D/TEEEST: call FirstSecond mapped thread main

第二轮:

D/TEEEST: 1 onNext First thread RxComputationScheduler-2
D/TEEEST: 2 onNext Second thread RxIoScheduler-2
D/TEEEST: zip func thread RxIoScheduler-2
D/TEEEST: map thread RxIoScheduler-2
D/TEEEST: call FirstSecond mapped thread main
  1. 这种行为是否记录在某处?
  2. 为什么会这样。
  3. 如何确保zip函数和所有下游内容(在我的情况下为map运算符)适用于特定的调度程序,而不是随机调度程序。

3 个答案:

答案 0 :(得分:4)

默认情况下,

zip不会在特定的Scheduler上运行。

zip只有在拥有所有要压缩的值时才会发出。所以它在它收到最后一个值的同一个线程上发出。

要确保在特定调度程序上执行所有下游操作,您必须定义observeOn()副作用。

对于所有下游操作,观察压缩结果就足够了。

Observable.zip(...).observeOn(scheduler)

对于上游,您必须观察在此特定调度程序上压缩的观察者。

Observable.zip(o1.observeOn(scheduler), o2.observeOn(scheduler), ...)

根据您要使用的调度程序,这不保证线程。

答案 1 :(得分:0)

RXJava的优点在于RxJava中的操作符在等待来自上游可观察对象的值时阻塞线程,而不是保持线程和阻塞,运算符重用上游观察者向其发出值的线程。

行为不仅限于zip。您可以观察与其他运算符相同的行为,例如:flatMap。

从提供的池中挑选线程,因为它们可用。

答案 2 :(得分:0)

我想修改@ tynn&#39;在他的许可下,我的代码很少!

假设我想执行以下两个功能。

fetchFromGoogle = fetchFromGoogle.subscribeOn(Schedulers.newThread());
fetchFromYahoo = fetchFromYahoo.subscribeOn(Schedulers.newThread());

这样我们就可以使用#zip operator,

// Fetch from both API 
Observable<String> zipped 
    = Observable.zip(fetchFromGoogle, fetchFromYahoo, new Func2<String, String, String>() {
        @Override
        public String call(String google, String yahoo) { 
            // Do something with the results of both threads
            return google + "\n" + yahoo;
        }
    });

此外,您可以使用#concat运算符逐个运行该线程。

Observable<String> concatenated = Observable.concat(fetchFromGoogle, fetchFromYahoo);
// Emit the results one after another