RX Switch()的订阅和取消订阅订单

时间:2015-10-24 22:44:08

标签: c# .net system.reactive reactive-programming

所以我写了一篇关于我遇到的问题的草稿。我有一个IObserverable<的IObservable< TResult>>其中包含我的流,我想用switch来获取它的最新项目,但是我可以用以下代码巧妙地证明我的问题:

        var obs = Observable.Create<IObservable<int>>(sub =>
        {
            var item = Observable.Create<int>(innersub =>
            {
                var count = 0;
                return Observable.Interval(TimeSpan.FromSeconds(2)).Subscribe(x => innersub.OnNext(count++));
            }).Publish().RefCount();

            return Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(x => sub.OnNext(item));
        });

        obs.Switch().Subscribe(x => Console.WriteLine(x));

上面的测试用例表明,当与Publish()。RefCount()一起使用时,交换机首先取消订阅然后订阅新项目。

我想要的是连续的数字流量上升,但是测试显示“新项目”在新订阅点击之前首先处理掉,我丢失了这个数字并且必须重新开始。

如果项目相同,并且使用了refcount,我想要的是首先发生订阅,所以refcount很高兴,然后处理旧的订阅。这种行为是RX可以默认显示还是需要一些躲避才能正确?我相信我可以根据RX源代码的缩减版本编写一个简单的扩展方法,但如果它已经存在或者我想先知道更好的方法。

编辑:编写的代码是一个以简单方式演示问题的简单示例。我实际拥有的是一个可观察的,它可以定期发布一个新的可观察对象,它有不同的过滤器,但最终可归结为基于它的所有可观察的相同发布/引用。 (where子句改变,或者select做了不同的事情。实际使用的是几个流的.Merge(),所以我对我的逻辑和问题的结论很有信心)。我很清楚我的例子可以简化。

3 个答案:

答案 0 :(得分:1)

你将不得不查看源代码,因为之前的observable在当前的observable订阅之前就被处理掉了。这就是.Switch()的工作方式。

如果Rx在新订阅之后处置,则代码的意图似乎相当于简单地执行此操作:

var obs = Observable.Create<int>(innersub =>
{
    var count = 0;
    return Observable.Interval(TimeSpan.FromSeconds(2))
        .Subscribe(x => innersub.OnNext(count++));
});

obs.Subscribe(x => Console.WriteLine(x));

在这个例子中,它归结为:

var obs = Observable.Interval(TimeSpan.FromSeconds(2));

obs.Subscribe(x => Console.WriteLine(x));

也许您可以告诉我们您的基本要求是什么,我们可以继续努力吗?

答案 1 :(得分:1)

此操作符主要用于冷观测值。 Switch在订阅新的observable之前取消订阅。否则会存在竞争条件,在订阅这两个事件的短暂时间内,额外事件可能会漏掉。

由于您的基础observable很热,您可以考虑另一种解决方案,您只需修改过滤器/选择“即时”而不是使用Switch来“重新订阅”。类似的东西:

source
    .Where(t => ApplyCurrentFilter(t))
    .Select(t => ApplyCurrentProjection(t))
    .Subscribe(...);

// do something that changes what `ApplyCurrentFilter` does...

我不知道这是否比您当前的解决方案更好或更差,但它确实避免了取消订阅/重新订阅源数据的需要。

答案 2 :(得分:0)

如前所述,Observable.Create会生成一个冷Observable,而Publish.RefCount只会让它变热,而那里仍有订阅者。在处理旧订阅服务器之前,可以编写自己的Switch订阅版本。但我对种族条件非常警惕。一般来说,感觉有点奇怪,在Rx中通常会发出另一种方式来表达您想要的更清洁。

在这个例子中,如果你有所需的结果,那么发布许多Observable并切换它们没有任何意义,因为实际上你只想在整个持续时间内订阅一个observable。因此,它归结为Enigmativity所说的。

然而,显然这是一个人为的例子,所以让我们假设需要这种方法的情况更复杂 - 如果你能够详细说明,它可能会有所帮助。从示例中,您似乎只想订阅一次内部observable。根据该要求,RefCount是不合适的,但我假设您正在使用它,因为您希望核心的共享可观察对象,您希望每次都要以不同的方式运行其他运算符。如果是这种情况,您可以使用这样的方法:

var obs = Observable.Create<IObservable<int>>(sub =>
{
   var item = Observable.Create<int>(innersub =>
   {
       var count = 0;
       return Observable.Interval(TimeSpan.FromSeconds(2))
                        .Subscribe(x => innersub.OnNext(count++));
   }).Publish();

   bool connected = false;
   var disposables = new CompositeDisposable();
   disposables.Add(Observable.Interval(TimeSpan.FromSeconds(10))
                             .Subscribe(x =>
                             {
                                 // push the new stream to the observer first
                                 sub.OnNext(item);

                                 if (!connected)
                                 {
                                     connected = true;
                                     disposables.Add(item.Connect());
                                 }
                             }));

   return disposables;
});

我还没有想过用这种方法等潜在的竞争条件,而且很大程度上取决于你的实际情况。但是,在原始帖子的基本测试中,这似乎表现得如你所愿。