Java 使用节流阀的组合式可流动件被卡住,原因不明

Java 使用节流阀的组合式可流动件被卡住,原因不明,java,rx-java2,Java,Rx Java2,经过组合测试后,我的可流动链条无法继续运行。最后,我尝试在一个单独的课堂上重现这种行为。我发现有点令人不安: public static void main(String[] args) throws InterruptedException { test(); test(); Thread.sleep(10000); } private static void test() { System.out.println("before"); Flowabl

经过组合测试后,我的可流动链条无法继续运行。最后,我尝试在一个单独的课堂上重现这种行为。我发现有点令人不安:

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");
}
publicstaticvoidmain(String[]args)抛出InterruptedException{
test();
test();
睡眠(10000);
}
专用静态空隙试验(){
系统输出打印项次(“之前”);
Flowable fa=Flowable.generate(发射器->发射器.onNext(1)).observeOn(Schedulers.computation()).throttleLast(1,TimeUnit.SECONDS);
FlowAbable fb=Flowable.generate(发射器->发射器.onNext(2)).observeOn(Schedulers.computation()).throttleLast(1,TimeUnit.SECONDS);
可流动。组合测试(fa,fb,(a,b)->a-b)
.subscribeOn(Schedulers.computation())
.订阅(c->{
System.out.println(“c:+c”);
});
System.out.println(“之后”);
}
前后两次之后,什么也没发生。但是,如果我注释掉两个test()调用中的一个,它就会正常工作。如果我将Schedulers.computation()替换为Schedulers.io(),则它可用于两个test()调用

我猜这个解释一定与每个核有一个计算线程的事实有关。即使这样,这种行为对我来说也没有任何意义


有谁能启发我吗?使用Schedulers.io()似乎不是一个好的解决方案。顺便说一下,我使用throttleLast是因为消费者必须能够控制吞吐量。

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

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"))
                ;
    }
}

你能用这段代码解释一下你的问题吗?

很有趣。看起来这种特殊的紧循环设置不会让
throttleLast
s有机会发射。我已经在我的4核机器上运行了好几次您的代码,偶尔会打印出
-1

您可以为
throttleLast
使用其他一些
调度器
,例如
调度器.single()
,这样
计算()
调度器就有点空闲了。然而,潜在的问题是紧循环,它可以在
计算
调度程序
中锁定线程,因此如果将同一线程分配给另一个紧循环工作,它可能无法在合理的时间内执行

通过添加一些
doOnNext
doOnSubscribe
调用,似乎大多数时间test-1的
fa
和test-2的
fb
都在发送数据,而不是分别发送
test-1-fb
test-2-fa
,因此
combinelatetest
由于缺少配对,不会产生任何值

编辑

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

静态FlowableTransformer requestObserveOn(调度程序){
返回f->newrequestobserveon(f,调度器);
}
静态最终类RequestObserveOn扩展了Flowable{
最终流动源;
最终调度器;
RequestObserveOn(可流动源、调度程序){
this.source=源;
this.scheduler=调度程序;
}
@凌驾

受保护的void subscribeActual(subscriber您的计算机有多少个内核?我得到的是不同的输出。37行而不是46行。最后:true false true如果调用test()您能测试会发生什么吗还有几次?我有4个内核,8个线程。我尝试运行了三次,但确实没有得到相同的结果!有没有关于紧循环的文档,我可以阅读以更好地理解这个问题?你同意这是一个bug吗?只需使用
.throttleFirst(1,TimeUnit.SECONDS,Schedulers.single())
似乎无法解决问题。没有文档,因为这是一个使用拐点:您有相同数量的快速生成源,它们不会放弃各自的线程,这些线程所做的任何其他工作几乎没有机会运行。这不是一个bug,而是
的一个模糊属性关于
以及如何分配调度程序
的问题。请尝试删除
subscribeOn
。发布了包含自定义运算符的答案更新,以解决您的用例。删除
subscribeOn
会有帮助。但是,我不明白为什么。此外,我感谢您的帮助,但我确实认为这不需要一个定制的解决方案。在我看来,这应该是可行的。这很有趣:它看起来很简单,但以非确定性的方式工作。
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
static <T> FlowableTransformer<T, T> requestObserveOn(Scheduler scheduler) {
    return f -> new RequestObserveOn<>(f, scheduler);
}

static final class RequestObserveOn<T> extends Flowable<T> {

    final Flowable<T> source;

    final Scheduler scheduler;

    RequestObserveOn(Flowable<T> source, Scheduler scheduler) {
        this.source = source;
        this.scheduler = scheduler;
    }

    @Override
    protected void subscribeActual(Subscriber<? super T> s) {
        source.subscribe(new RequestObserveOnSubscriber<>(s, scheduler.createWorker()));
    }

    static final class RequestObserveOnSubscriber<T> 
    extends AtomicLong
    implements FlowableSubscriber<T>, Subscription, Runnable {

        private static final long serialVersionUID = 3167152788131496136L;

        final Subscriber<? super T> actual;

        final Worker worker;

        final Runnable requestOne;

        Subscription upstream;

        volatile T item;
        Throwable error;
        volatile boolean done;

        long emitted;
        boolean terminated;

        RequestObserveOnSubscriber(Subscriber<? super T> actual,
                Scheduler.Worker worker) {
            this.actual = actual;
            this.worker = worker;
            this.requestOne = () -> upstream.request(1L);
        }

        @Override
        public void onSubscribe(Subscription s) {
            upstream = s;
            actual.onSubscribe(this);
            worker.schedule(requestOne);
        }

        @Override
        public void onNext(T t) {
            item = t;
            worker.schedule(this);
        }

        @Override
        public void onError(Throwable t) {
            error = t;
            done = true;
            worker.schedule(this);
        }

        @Override
        public void onComplete() {
            done = true;
            worker.schedule(this);
        }

        @Override
        public void run() {
            if (terminated) {
                return;
            }
            for (;;) {
                boolean d = done;
                T v = item;
                boolean empty = v == null;

                if (d && empty) {
                    Throwable ex = error;
                    if (ex != null) {
                        actual.onError(ex);
                    } else {
                        actual.onComplete();
                    }
                    worker.dispose();
                    terminated = true;
                    return;
                }
                long e = emitted;
                if (!empty && e != get()) {
                    item = null;
                    actual.onNext(v);
                    emitted = e + 1;
                    worker.schedule(requestOne);
                } else {
                    break;
                }
            }
        }

        @Override
        public void request(long n) {
            BackpressureHelper.add(this, n);
            worker.schedule(this);
        }

        @Override
        public void cancel() {
            upstream.cancel();
            worker.dispose();
            item = null;
        }
    }
}