web-dev-qa-db-ja.com

RxJavaとオブザーバーコードの並列実行

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 -> {
      }, 
      () -> {
      });

私の期待は、計算スケジューラを指定した後、観測コード、つまりsubscribe()メソッド内のコードが並列に実行されることでした。代わりに、コードは引き続き単一のスレッドで順次実行されます。 RxJava APIを使用してコードを並列に実行する方法。

25
Pawan Mishra

RxJavaは、非同期/マルチスレッドの側面に関して誤解されることがよくあります。マルチスレッド操作のコーディングは簡単ですが、抽象化を理解することは別のことです。

RxJavaに関する一般的な質問は、並列化を実現する方法、またはObservableから複数のアイテムを同時に出力する方法です。もちろん、この定義は、onNext()が一度に複数のスレッドによって同時に呼び出されることはなく、同時に呼び出される必要があることを示すObservable Contractを破ります。

並列処理を実現するには、複数のObservableが必要です。

これは単一のスレッドで実行されます:

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()));

これは複数のスレッドで実行されます:

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

コードとテキスト は、このブログ投稿からのものです。

53
LordRaydenMK

RxJava 2.0.5では、 paralellフロー および ParallelFlowable が導入されました。これにより、並列実行がより簡単で宣言的になります。

Observable内にFlowable/flatMapを作成する必要はなくなりました。Flowableparallel()を呼び出すだけで、ParallelFlowable

同時実行はRxコントラクトで多くの問題を引き起こすため、通常のFlowableほど機能が豊富ではありませんが、基本的なmap()filter()などがあります。ほとんどの場合で十分です。

@LordRaydenMKからのこのフローの代わりに答え

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

できるようになりました:

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

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

そのためには、subscribeOn(Schedulers.computation())の代わりにobserveOn(Schedulers.computation())を指定する必要があります。 subscribeOnで、値を出力するスレッドを宣言します。 observeOnで、どのスレッドでそれらを処理および監視するかを宣言します。

2
Geralt_Encore

flatMapを使用し、Schedulers.computation()をサブスクライブするように指定すると、並行性が実現します。

出力からのCallableを使用したより実用的な例は、すべてのタスクを完了するのに約2000ミリ秒かかることがわかります。

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

これは同じシーケンスで行われます。新しいスレッドでも

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();

出力1 ccc RxNewThreadScheduler-1 2 ccc RxNewThreadScheduler-1 3 ccc RxNewThreadScheduler-1 4 ccc RxNewThreadScheduler-1 5 ccc RxNewThreadScheduler-1

0
Arun