RxJava - SwitchMap与多个有限的活动流相似

时间:2017-01-30 07:57:52

标签: concurrency rx-java

我想知道如何将一个observable变换为类似于switchMap,而不是限制为单个活动流有多个(有限的)流。

目的是让多个任务同时工作到某些任务计数限制,并允许新任务以FIFO队列策略启动,这意味着任何新任务到达将立即开始,队列中最旧的任务将被取消。

switchMap将为源的每次发射创建Observable,并且一旦创建新的一个流就会取消先前运行的Observable流,我想实现类似但允许某种程度的并发(如flatMap),这意味着允许创建多个Observable对于每个发射,并且同时运行到某个并发限制,当达到并发限制时,最旧的observable将被取消,新的observable将启动。

实际上,这也与使用maxConcurrent的flatMap类似,但是当达到maxConcurrent时,不是在队列中等待新的Observable,而是取消旧的Observable并立即输入新的Observable。

2 个答案:

答案 0 :(得分:3)

你可以试试这个变压器:

public static <T, R> Observable.Transformer<T, R> switchFlatMap(
        int n, Func1<T, Observable<R>> mapper) {
    return f -> 
        Observable.defer(() -> {
            final AtomicInteger ingress = new AtomicInteger();
            final Subject<Integer, Integer> cancel = 
                    PublishSubject.<Integer>create().toSerialized();

            return f.flatMap(v -> {
                int id = ingress.getAndIncrement();
                Observable<R> o = mapper.call(v)
                        .takeUntil(cancel.filter(e -> e == id + n));
                cancel.onNext(id);
                return o;
            });
        })
    ;
}

示威:

public static void main(String[] args) {
    PublishSubject<Integer> ps = PublishSubject.create();

    @SuppressWarnings("unchecked")
    PublishSubject<Integer>[] pss = new PublishSubject[3];
    for (int i = 0; i < pss.length; i++) {
        pss[i] = PublishSubject.create();
    }

    AssertableSubscriber<Integer> ts = ps
    .compose(switchFlatMap(2, v -> pss[v]))
    .test();

    ps.onNext(0);
    ps.onNext(1);

    pss[0].onNext(1);
    pss[0].onNext(2);
    pss[0].onNext(3);

    pss[1].onNext(10);
    pss[1].onNext(11);
    pss[1].onNext(12);

    ps.onNext(2);

    pss[0].onNext(4);

    pss[2].onNext(20);
    pss[2].onNext(21);
    pss[2].onNext(22);

    pss[1].onCompleted();
    pss[2].onCompleted();
    ps.onCompleted();

    ts.assertResult(1, 2, 3, 10, 11, 12, 20, 21, 22);
}

答案 1 :(得分:0)

虽然现成的解决方案不可用,但下面的内容应该有所帮助。

public static void main(String[] args) {

    Observable.create(subscriber -> {
                for (int i = 0; i < 5; i++) {
                    Observable.timer(i, TimeUnit.SECONDS).toBlocking().subscribe();
                    subscriber.onNext(i);
                }
            })
            .switchMap(
                    n -> {
                        System.out.println("Main task emitted event - " + n);
                        return Observable.interval(1, TimeUnit.SECONDS).take((int) n * 3)
                                .doOnUnsubscribe(() -> System.out.println("Unsubscribed for main task event - "+ n));
                    }).subscribe(n2 -> System.out.println("\t" + n2));

    Observable.timer(20, TimeUnit.SECONDS).toBlocking().subscribe();
}

Observable.create部分创建一个缓慢的生成器,以发出0的方式发出项目,睡眠1秒并发出1,睡眠2秒,发出2等等。

switchMap为每个元素创建Observable个对象,每秒发出一次数字。您还可以注意到,每次主Observable发出元素时以及取消订阅时,它都会打印一行。

因此,在您的情况下,您可能有兴趣使用doOnUnsubscribe关闭最早的任务。希望它有所帮助。

伪代码下面可能有助于更好地理解。

getTaskObservable()
        .switchMap(
                task -> {
                    System.out.println("Main task emitted event - " + task);
                    return Observable.create(subscriber -> {
                        initiateTaskAndNotify(task, subscriber);
                    }).doOnUnsubscribe(() -> checkAndKillIfMaxConcurrentTasksReached(task));
                }).subscribe(value -> System.out.println("Done with task and got output" + value));