使用throttleLast的组合Flowable没有任何理由被卡住

时间:2018-01-27 17:44:27

标签: java rx-java2

在一个combineLatest后,我的Flowable链条没有继续运行。最后,我试图在一个单独的类中重现行为。我发现的有点令人不安:

public static void main(String[] args) throws InterruptedException {
    test();
    test();
    Thread.sleep(10000);
}

private static void test() {
    System.out.println("before");
    Flowable<Integer> fa = Flowable.<Integer>generate(emitter -> emitter.onNext(1)).observeOn(Schedulers.computation()).throttleLast(1, TimeUnit.SECONDS) ;
    Flowable<Integer> fb = Flowable.<Integer>generate(emitter -> emitter.onNext(2)).observeOn(Schedulers.computation()).throttleLast(1, TimeUnit.SECONDS) ;
    Flowable.combineLatest(fa, fb, (a, b) -> a - b)
            .subscribeOn(Schedulers.computation())
            .subscribe(c -> {
                System.out.println("c: " + c);
            });
    System.out.println("after");
}

之前/之后两次,没有任何事情发生。但是,如果我注释掉两个test()调用中的一个,它就可以了。如果我用Schedulers.io()替换Schedulers.computation(),它可以用于test()调用。

我想解释必须与每个核心有一个计算线程这一事实有关。即便如此,这种行为对我来说也没有任何意义。

任何人都可以开导我吗?使用Schedulers.io()似乎不是一个好的解决方案。顺便说一下,我使用throttleLast,因为消费者必须能够控制吞吐量。

2 个答案:

答案 0 :(得分:1)

我不完全理解你的问题。我尝试了以下更改代码:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

import com.google.common.collect.Sets;

import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;

public class TestRxJava2 {

    static final Queue<Integer> fa1 = new ConcurrentLinkedQueue<Integer>();
    static final Queue<Integer> fb1 = new ConcurrentLinkedQueue<Integer>();
    static final Queue<Integer> c1 = new ConcurrentLinkedQueue<Integer>();
    static final Queue<Integer> fa2 = new ConcurrentLinkedQueue<Integer>();
    static final Queue<Integer> fb2 = new ConcurrentLinkedQueue<Integer>();
    static final Queue<Integer> c2 = new ConcurrentLinkedQueue<Integer>();

    public static void main(String[] args) throws InterruptedException {
        Flowable<Integer> f1 = test(1);
        Flowable<Integer> f2 = test(2);

        Flowable.zip(f1, f2, (a,b) -> a)
            .blockingSubscribe();

        System.out.println(fa1.size() == 10 && fa1.size() == fa2.size());
        System.out.println(fb1.size() == 10 && fb1.size() == fb2.size());
        System.out.println(c1.size() == 19 && c1.size() == c2.size());

        System.out.println(Sets.difference(Sets.newHashSet(fa1), Sets.newHashSet(fa2)).size() == 0);
        System.out.println(Sets.difference(Sets.newHashSet(fb1), Sets.newHashSet(fb2)).size() == 0);
        System.out.println(Sets.difference(Sets.newHashSet(c1), Sets.newHashSet(c2)).size() == 0);

    }

    private static Flowable<Integer> test(int i) {

        Flowable<Integer> fa = Flowable.<Integer>generate(emitter -> emitter.onNext(1))
                .observeOn(Schedulers.computation())
                .throttleLast(1, TimeUnit.SECONDS)
                .take(10)
                .scan((a, b) -> a + b)
                .doOnNext(next -> {
                    if (i == 1) {
                        fa1.add(next);
                    } else {
                        fa2.add(next);
                    }
                });

        Flowable<Integer> fb = Flowable.<Integer>generate(emitter -> emitter.onNext(1))
                .observeOn(Schedulers.computation())
                .throttleLast(1, TimeUnit.SECONDS)
                .take(10)
                .scan((a, b) -> a + b)
                .doOnNext(next -> {
                    if (i == 1) {
                        fb1.add(next);
                    } else {
                        fb2.add(next);
                    }
                });

        return Flowable.combineLatest(fa, fb, (a, b) -> a + b)
                .subscribeOn(Schedulers.computation())
                .doOnNext(next -> {
                    if (i == 1) {
                        c1.add(next);
                    } else {
                        c2.add(next);
                    }
                })
                .doOnNext(next -> System.out.println(i + "CombineLatest : " + next))
                .doOnSubscribe(sb -> System.out.println(i + " subscribed"))
                ;
    }
}

控制台输出如下:

1 subscribed
2 subscribed
1CombineLatest : 2
2CombineLatest : 2
1CombineLatest : 3
2CombineLatest : 3
1CombineLatest : 4
2CombineLatest : 4
1CombineLatest : 5
2CombineLatest : 5
1CombineLatest : 6
2CombineLatest : 6
1CombineLatest : 7
2CombineLatest : 7
1CombineLatest : 8
2CombineLatest : 8
1CombineLatest : 9
2CombineLatest : 9
1CombineLatest : 10
2CombineLatest : 10
1CombineLatest : 11
2CombineLatest : 11
1CombineLatest : 12
2CombineLatest : 12
1CombineLatest : 13
2CombineLatest : 13
1CombineLatest : 14
2CombineLatest : 14
1CombineLatest : 15
2CombineLatest : 15
1CombineLatest : 16
2CombineLatest : 16
1CombineLatest : 17
2CombineLatest : 17
1CombineLatest : 18
2CombineLatest : 18
1CombineLatest : 19
2CombineLatest : 19
1CombineLatest : 20
2CombineLatest : 20
true
true
true
true
true
true

您能否就此代码进行解释,您的问题到底是什么?

答案 1 :(得分:1)

有趣。看起来这个特殊的紧密循环设置不会让throttleLast有机会发射。我已经在我的4核机器上多次运行你的代码,偶尔我会打印出-1

您可以对Scheduler使用其他throttleLast,例如Schedulers.single(),以便释放computation() Scheduler。然而,潜在的问题是紧密循环,它可以挂钩computation Scheduler中的一个线程,所以如果将同一个线程分配给另一个紧密循环工作,它可能无法在一个合理的范围内执行时间。

通过添加一些doOnNextdoOnSubscribe来电,似乎大部分时间test-1 fa和test-2 fb正在发送数据但不是{分别为{1}}和test-1-fb,因此test-2-fa由于缺少配对而不会产生任何值。

修改

该问题的一个特性是combineLatest针对生成器线程和所选发射线程之间的持续流进行了优化,在某些情况下可能会固定两个线程。通过将每个源发射和earch请求转换为自己的任务并依赖底层线程池的公平性,还可以以更加线程使用友好的方式在线程之间切换。以下自定义运算符应取消阻止您的用例。将observeOn替换为observeOn(Schedulers.computation())

compose(requestObserveOn(Schedulers.computation()))