繁体   English   中英

使用RxJava生成无限序列的自然数

[英]Generate infinite sequence of Natural numbers using RxJava

我正在尝试使用RxJava编写一个简单的程序来生成无限的自然数序列。 所以,到目前为止,我已经找到了两种使用Observable.timer()Observable.interval()生成数字序列的方法。 我不确定这些功能是否是解决此问题的正确方法。 我期待像Java 8中那样的简单函数来生成无限的自然数。

IntStream.iterate(1,value - > value +1)。forEach(System.out :: println);

我尝试使用带有Observable的IntStream,但这不能正常工作。 它只向第一个用户发送无限的数字流。 如何正确生成无限自然数序列?

import rx.Observable;
import rx.functions.Action1;

import java.util.stream.IntStream;

public class NaturalNumbers {

    public static void main(String[] args) {
        Observable<Integer> naturalNumbers = Observable.<Integer>create(subscriber -> {
            IntStream stream = IntStream.iterate(1, val -> val + 1);
            stream.forEach(naturalNumber -> subscriber.onNext(naturalNumber));
        });

        Action1<Integer> first = naturalNumber -> System.out.println("First got " + naturalNumber);
        Action1<Integer> second = naturalNumber -> System.out.println("Second got " + naturalNumber);
        Action1<Integer> third = naturalNumber -> System.out.println("Third got " + naturalNumber);
        naturalNumbers.subscribe(first);
        naturalNumbers.subscribe(second);
        naturalNumbers.subscribe(third);

    }
}

问题是naturalNumbers.subscribe(first); ,你实现的OnSubscribe被调用,你正在一个无限的流上做一个forEach ,因此你的程序永远不会终止。

您可以处理它的一种方法是在不同的线程上异步订阅它们。 为了轻松查看结果,我不得不在Stream处理中引入一个sleep:

Observable<Integer> naturalNumbers = Observable.<Integer>create(subscriber -> {
    IntStream stream = IntStream.iterate(1, i -> i + 1);
    stream.peek(i -> {
        try {
            // Added to visibly see printing
            Thread.sleep(50);
        } catch (InterruptedException e) {
        }
    }).forEach(subscriber::onNext);
});

final Subscription subscribe1 = naturalNumbers
    .subscribeOn(Schedulers.newThread())
    .subscribe(first);
final Subscription subscribe2 = naturalNumbers
    .subscribeOn(Schedulers.newThread())
    .subscribe(second);
final Subscription subscribe3 = naturalNumbers
    .subscribeOn(Schedulers.newThread())
    .subscribe(third);

Thread.sleep(1000);

System.out.println("Unsubscribing");
subscribe1.unsubscribe();
subscribe2.unsubscribe();
subscribe3.unsubscribe();
Thread.sleep(1000);
System.out.println("Stopping");

Observable.Generate正是反应性地解决这类问题的运算符。 我还假设这是一个教学示例,因为使用迭代可能更好。

您的代码在订阅者的线程上生成整个流。 由于它是无限流,因此subscribe呼叫永远不会完成。 除了那个明显的问题之外,取消订阅也会有问题,因为你没有在你的循环中检查它。

您希望使用调度程序来解决此问题 - 当然不要使用subscribeOn因为这会给所有观察者带来负担。 将每个号码的发送安排到onNext - 作为每个预定操作的最后一步,安排下一个。

基本上这就是Observable.generate为您提供的 - 每次迭代都在提供的调度程序上进行调度(如果您没有指定它,则默认为引入并发的调度程序)。 可以取消调度程序操作并避免线程不足。

Rx.NET像这样解决它(实际上有一个async/await模型更好,但在Java afaik中不可用):

static IObservable<int> Range(int start, int count, IScheduler scheduler)
{
    return Observable.Create<int>(observer =>
    {
        return scheduler.Schedule(0, (i, self) =>
        {
            if (i < count)
            {
                Console.WriteLine("Iteration {0}", i);
                observer.OnNext(start + i);
                self(i + 1);
            }
            else
            {
                observer.OnCompleted();
            }
        });
   });
}

这里有两点需要注意:

  • 对Schedule的调用返回一个传递回观察者的订阅句柄
  • Schedule是递归的 - self参数是对用于调用下一次迭代的调度程序的引用。 这允许取消订阅取消操作。

不确定这在RxJava中看起来如何,但这个想法应该是一样的。 同样, Observable.generate可能对您来说更简单,因为它旨在处理这种情况。

在创建无限顺序时,应注意:

  1. 在不同的线程上订阅和观察; 否则你只会为单个用户服务
  2. 订阅终止后立即停止生成值; 否则失控循环会占用你的CPU

第一个问题是通过使用subscribeOn()observeOn()和各种调度程序来解决的。

第二个问题最好通过使用库提供的方法Observable.generate()Observable.fromIterable() 他们做了适当的检查。

检查一下:

Observable<Integer> naturalNumbers =
        Observable.<Integer, Integer>generate(() -> 1, (s, g) -> {
            logger.info("generating {}", s);
            g.onNext(s);
            return s + 1;
        }).subscribeOn(Schedulers.newThread());
Disposable sub1 = naturalNumbers
        .subscribe(v -> logger.info("1 got {}", v));
Disposable sub2 = naturalNumbers
        .subscribe(v -> logger.info("2 got {}", v));
Disposable sub3 = naturalNumbers
        .subscribe(v -> logger.info("3 got {}", v));

Thread.sleep(100);

logger.info("unsubscribing...");
sub1.dispose();
sub2.dispose();
sub3.dispose();
Thread.sleep(1000);

logger.info("done");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM