关于并发:RxJava-具有多个有限活动流的SwitchMap

RxJava - SwitchMap alike with multiple limited active streams

我想知道如何转换类似于switchMap的可观察对象,但不限于具有多个(有限)流的单个活动流。

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

switchMap将为源的每次发射创建Observable流,并在创建新的Observable流时取消先前运行的Observable流,我想实现类似的功能,但允许一定级别的并发(例如flatMap),这意味着允许创建Observable的数量对于每个发射,并发运行并达到一定的并发限制,当达到并发限制时,最早的可观察对象将被取消,而新的可观察对象将开始。

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


您可以尝试使用此变压器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
            });
        })
    ;
}

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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,hibernate1s,发出1,hibernate2s,发出2等等。

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

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

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

1
2
3
4
5
6
7
8
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));