web-dev-qa-db-fra.com

RxJava et exécution parallèle du code de l'observateur

J'ai le code suivant en utilisant RxJava Observable api:

Observable<Info> observable = fileProcessor.processFileObservable(processedFile.getAbsolutePath());
    observable
      .buffer(10000)
      .observeOn(Schedulers.computation())
      .subscribe(recordInfo -> {
        _logger.info("Running stage2 on thread with id : " + Thread.currentThread().getId());
          for(Info info : recordInfo) {
            // some I/O operation logic
         }
      }, 
      exception -> {
      }, 
      () -> {
      });

J'espérais que le code d'observation, c'est-à-dire le code à l'intérieur de la méthode subscribe (), sera exécuté en parallèle après avoir spécifié le planificateur de calcul. Au lieu de cela, le code est toujours exécuté séquentiellement sur un seul thread. Comment faire en sorte que le code soit exécuté en parallèle avec RxJava api.

18
Pawan Mishra

RxJava est souvent mal compris en ce qui concerne les aspects asynchrones/multithread. Le codage des opérations multithread est simple, mais comprendre l'abstraction est une autre chose.

Une question commune à propos de RxJava est de savoir comment réaliser la parallélisation ou émettre plusieurs éléments simultanément à partir d’un observable. Bien sûr, cette définition rompt le contrat observable qui stipule que onNext () doit être appelé de manière séquentielle et jamais simultanément par plusieurs threads à la fois. 

Pour réaliser le parallélisme, vous avez besoin de plusieurs observables.

Cela fonctionne dans un seul thread:

Observable<Integer> vals = Observable.range(1,10);

vals.subscribeOn(Schedulers.computation())
          .map(i -> intenseCalculation(i))
          .subscribe(val -> System.out.println("Subscriber received "
                  + val + " on "
                  + Thread.currentThread().getName()));

Cela fonctionne dans plusieurs threads:

Observable<Integer> vals = Observable.range(1,10);

vals.flatMap(val -> Observable.just(val)
            .subscribeOn(Schedulers.computation())
            .map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));

Code et texte proviennent de cet article de blog.

45
LordRaydenMK

RxJava 2.0.5 introduit des flux parallèles et ParallelFlowable , ce qui rend l’exécution en parallèle plus simple et plus déclarative. 

Vous n'avez plus besoin de créer Observable/Flowable dans flatMap, vous pouvez simplement appeler parallel() sur Flowable et il retourne ParallelFlowable

Ce n'est pas aussi riche en fonctionnalités qu'une Flowable régulière, car la simultanéité pose de nombreux problèmes avec les contrats Rx, mais vous avez les fonctions de base map(), filter() et bien d'autres, qui devraient suffire dans la plupart des cas. 

Donc, au lieu de ce flux de @LordRaydenMK answer

Observable<Integer> vals = Observable.range(1,10);

vals.flatMap(val -> Observable.just(val)
        .subscribeOn(Schedulers.computation())
        .map(i -> intenseCalculation(i))
    ).subscribe(val -> System.out.println(val));

maintenant tu peux faire:

Flowable<Integer> vals = Flowable.range(1, 10);

vals.parallel()
        .runOn(Schedulers.computation())
        .map(i -> intenseCalculation(i))
        .sequential()
        .subscribe(val -> System.out.println(val));
7
michalbrz

Vous devez spécifier subscribeOn(Schedulers.computation()) au lieu de observeOn(Schedulers.computation()) à cette fin .. Dans subscribeOn, vous déclarez dans quel fil vous allez émettre vos valeurs .. Dans observeOn, vous déclarez dans quel fil vous allez les gérer.

2
Geralt_Encore

Utiliser flatMap et spécifier de s'abonner sur Schedulers.computation() permettra d'obtenir une concurrence.

Voici un exemple plus pratique utilisant Callable. Nous pouvons voir dans le résultat qu'il faudra environ 2000 millisecondes pour terminer toutes les tâches.

static class MyCallable implements Callable<Integer> {

    private static final Object CALLABLE_COUNT_LOCK = new Object();
    private static int callableCount;

    @Override
    public Integer call() throws Exception {
        Thread.sleep(2000);
        synchronized (CALLABLE_COUNT_LOCK) {
            return callableCount++;
        }
    }

    public static int getCallableCount() {
        synchronized (CALLABLE_COUNT_LOCK) {
            return callableCount;
        }
    }
}

private static void runMyCallableConcurrentlyWithRxJava() {
    long startTimeMillis = System.currentTimeMillis();

    final Semaphore semaphore = new Semaphore(1);
    try {
        semaphore.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Observable.just(new MyCallable(), new MyCallable(), new MyCallable(), new MyCallable())
            .flatMap(new Function<MyCallable, ObservableSource<?>>() {
                @Override
                public ObservableSource<?> apply(@NonNull MyCallable myCallable) throws Exception {
                    return Observable.fromCallable(myCallable).subscribeOn(Schedulers.computation());
                }
            })
            .subscribeOn(Schedulers.computation())
            .subscribe(new Observer<Object>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {

                }

                @Override
                public void onNext(@NonNull Object o) {
                    System.out.println("onNext " + o);
                }

                @Override
                public void onError(@NonNull Throwable e) {

                }

                @Override
                public void onComplete() {
                    if (MyCallable.getCallableCount() >= 4) {
                        semaphore.release();
                    }
                }
            });


    try {
        semaphore.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        semaphore.release();
    }
    System.out.println("durationMillis " + (System.currentTimeMillis()-startTimeMillis));
}
1
alijandro

Cela vient toujours sur la même séquence. Même sur de nouveaux fils

Observable ob3 = Observable.range (1, 5);

    ob3.flatMap(new Func1<Integer, Observable<Integer>>() {

        @Override
        public Observable<Integer> call(Integer pArg0) {

            return Observable.just(pArg0);
        }

    }).subscribeOn(Schedulers.newThread()).map(new Func1<Integer, Integer>() {

        @Override
        public Integer call(Integer pArg0) {

            try {
                Thread.sleep(1000 - (pArg0 * 100));
                System.out.println(pArg0 + "  ccc   " + Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }

            return pArg0;
        }

    }).subscribe();

Sortie 1 ccc RxNewThreadScheduler-1 2 ccc RxNewThreadScheduler-1 3 ccc RxNewThreadScheduler-1 4 ccc RxNewThreadScheduler-1 5 ccc RxNewThreadScheduler-1

0
Arun