简体   繁体   English

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

[英]RX Switch()'s Subscribe and Unsubscribe Order

So I have written a rough draft of the problem I'm encountering. 所以我写了一篇关于我遇到的问题的草稿。 I have an IObserverable< IObservable< TResult>> which contains my stream, and I would like to use switch to get the latest items from it, however the problem I have can be neatly demonstrated with the code below: 我有一个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));

The above test case shows that when used in conjunction with Publish().RefCount() the switch first unsubscribes and then subscribes to the new item. 上面的测试用例表明,当与Publish()。RefCount()一起使用时,交换机首先取消订阅然后订阅新项目。

What I would like is for a continuous stream of numbers going up, however the test shows the "item" is disposed of first before the new subscription hits and I lose that count and have to start again. 我想要的是连续的数字流量上升,但是测试显示“新项目”在新订阅命中之前被丢弃,我丢失了这个数字并且必须重新开始。

If the item is the same, and refcount is used, what I would like is for the subscription to happen first, so refcount is happy, and then the old subscription disposed of. 如果项目是相同的,并且使用了refcount,我想要的是首先发生订阅,所以refcount很高兴,然后处理旧的订阅。 Is this behavior that RX can demonstrate by default or would it require some bodging to get right? 这种行为是RX可以默认显示还是需要一些躲避才能正确? I'm confident I could write a simple enough extension method based on a cut down version of the RX source code but if it already exists or there is a better way I'd like to know first. 我有信心我可以根据RX源代码的缩减版本编写一个简单的扩展方法,但如果它已经存在或者我想先了解更好的方法。

Edit: The code written was a naive example to demonstrate the problem in a simple way. 编辑:编写的代码是一个简单的例子来以简单的方式演示问题。 What I actually have is an observable that publishes a new observable regularly, which has different filters on it, but which ultimately boils down to the same publish/refcount observable at the base of it all. 我实际拥有的是一个可观察的,它可以定期发布一个新的可观察对象,它有不同的过滤器,但最终可归结为基于它的所有可观察的相同发布/引用。 (The where clause changes, or the select does something different. The real use is a .Merge() of several streams so I'm confident in my logic and my conclusion of the problem). (where子句改变,或者select做了不同的事情。实际使用的是几个流的.Merge(),所以我对我的逻辑和问题的结论很有信心)。 I'm well aware that my example could be simplified. 我很清楚我的例子可以简化。

You're going to have to look at the source as the previous observable gets disposed before the current one gets subscribed to. 你将不得不查看源代码,因为之前的observable在当前的一个observable被订阅之前被释放了。 That's how .Switch() works. 这就是.Switch()工作方式。

If Rx disposed after the new subscription, the intent of your code appears to be the equivalent of simply doing this: 如果在新订阅之后处置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));

And in this example it then boils down to: 在这个例子中,它归结为:

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

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

Perhaps you could let us know what your underlying requirement is and we could work on that? 也许您可以告诉我们您的基本要求是什么,我们可以继续努力吗?

This operator is designed primarily for cold observables. 该操作符主要用于冷观测值。 Switch unsubscribes from the previous observable before subscribing to the new one. 在订阅新的observable之前Switch取消订阅前一个observable。 Otherwise there would exist a race condition where extra events might slip through during the brief period it was subscribed to both. 否则将存在竞争条件,其中额外事件可能在订阅两者的短暂时期内漏掉。

Since your underlying observable is hot, you might consider an alternative solution where you just modify the filter/select "on the fly" instead of using Switch to "re-subscribe". 由于您的基础observable很热,您可能会考虑另一种解决方案,您只需修改过滤器/选择“即时”而不是使用Switch来“重新订阅”。 Something like: 就像是:

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

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

I don't know if this is better or worse than your current solution, but it does avoid the need to unsubscribe/resubscribe from source data. 我不知道这是否比您当前的解决方案更好或更差,但它确实避免了取消订阅/重新订阅源数据的需要。

As has been said, Observable.Create produces a cold Observable, and Publish.RefCount only makes it hot while there are still subscribers there. 如前所述,Observable.Create生成一个冷Observable,而Publish.RefCount只会让它变热,而那里仍有订阅者。 It would be possible to write your own version of Switch that subscribed the new subscriber before disposing the old. 在处理旧订阅服务器之前,可以编写自己的Switch订阅版本。 But I'd be very wary of race conditions. 但我对种族状况非常警惕。 And in general, it feels a bit of a weird thing to do, which in Rx usually signals there's another way to do what you want that's cleaner. 一般来说,感觉有点奇怪,在Rx中通常会发出另一种方式来做你想要的更干净的东西。

In this example, if you have the desired result, there's no purpose to publishing many Observables and Switching them because really you just want to be subscribed to one observable for the full duration. 在这个例子中,如果你有所需的结果,那么发布许多Observable并切换它们是没有意义的,因为实际上你只想在整个持续时间内订阅一个observable。 Hence how it boils down to what Enigmativity says. 因此,它归结为Enigmativity所说的。

However, obviously it's a contrived example so let's assume there's a more complicated situation that requires this approach - it may help if you're able to elaborate. 然而,显然这是一个人为的例子,所以让我们假设有一个更复杂的情况需要这种方法 - 如果你能够详细说明它可能会有所帮助。 From the example, it seems you only want to subscribe to the inner observable once, ever. 从示例中,您似乎只想订阅一次内部observable。 Based on that requirement, RefCount is not appropriate, but I assume you're using it because you want a shared observable at the core, which you're wrapping with other operators that you want to act differently each time. 基于该要求,RefCount是不合适的,但我假设您正在使用它,因为您希望在核心处拥有共享的可观察对象,您要与其他每次都要采取不同行为的运算符进行包装。 If that's the case, you could possibly use an approach like this: 如果是这种情况,您可以使用这样的方法:

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

I haven't thought through potential race conditions with this approach, etc, and a lot depends on your real situation. 我没有想过用这种方法等潜在的竞争条件,而且很大程度上取决于你的实际情况。 However, in the basic test from the original post, this seems to behave how you want it to. 但是,在原始帖子的基本测试中,这似乎表现得如何。

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

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