RX误解了行为

时间:2014-03-25 02:54:38

标签: system.reactive

我有以下的repro代码,它在更复杂的流程中展示了一个问题:

static void Main(string[] args)
    {
        var r = Observable.Range(1, 10).Finally(() => Console.WriteLine("Disposed"));
        var x = Observable.Create<int>(o =>
            {
                for (int i = 1; i < 11; i++)
                {
                    o.OnNext(i);
                }

                o.OnCompleted();

                return Disposable.Create(() => Console.WriteLine("Disposed"));
            });

        var src = x.Publish().RefCount();

        var a = src.Where(i => i % 2 == 0).Do(i => Console.WriteLine("Pair:" + i));
        var b = src.Where(i => i % 2 != 0).Do(i => Console.WriteLine("Even:" + i));

        var c = Observable.Merge(a, b);

        using (c.Subscribe(i => Console.WriteLine("final " + i), () => Console.WriteLine("Complete")))
        {
            Console.ReadKey();
        }
    }

使用r作为src(var src = r.Publish().RefCount())运行此代码段将生成从1到10的所有数字, 将src切换为x(如示例中所示)将仅生成对,实际上是第一个可以订阅的observ,除非我将Publish()更改为Replay()。

为什么呢? r和x有什么区别?

感谢。

2 个答案:

答案 0 :(得分:3)

虽然我没有耐心对Rx.NET源代码进行排序,以确切了解哪些实现细节会导致这种确切的行为,但我可以提供以下见解:

您所看到的行为差异是由竞争条件引起的。在这种情况下,竞争者是ab的订阅,这是因为您订阅Observable.Merge返回的可观察量而发生的。您订阅了c,后者又订阅了abab根据PublishRefCount的{​​{1}}和x来定义,具体取决于您选择的是哪种情况。< / p>

这是发生了什么。

src = r

在这种情况下,您使用的是自定义Observable。订阅时,您的自定义可观察 同步开始r数字1到10,然后调用onNext。有趣的是,此订阅是由onCompleted Observable在第一次订阅 时引起的。它是Publish().RefCount()第一次订阅,因为aa的第一个参数。因此,在Merge订阅Merge之前,您的订阅已经完成。 b订阅Merge,即b可观察对象。该observable已经完成,因此RefCount查找要合并的下一个Observable。由于没有更多的Observables要合并,并且因为所有现有的Observable都已完成,所以合并的observable完成了。

通过你的自定义observable接下来的值已经遍历了“对”可观察对象,而不是“均匀”可观察对象。因此,您最终得到以下结果:

Merge

src = x

在这种情况下,您使用内置的// "pairs" (has this been named incorrectly?) [2, 4, 6, 8, 10] 方法创建Observable。订阅时,此Range Observable 会执行最终会产生数字1到10 的内容。有趣。我们不知道该方法中发生了什么,或者何时发生。但是,我们可以对此进行一些观察。如果我们看一下Range(上图)时会发生什么,我们可以看到只有第一个订阅生效,因为observable正在立即和同步地产生 。因此,我们可以确定Range Observable不能以相同的方式产生,而是允许应用程序的控制流在产生任何值之前执行对src = r 的订阅。您的自定义Observable与此Range Observable之间的区别可能是Range Observable 调度b调度程序上发生的收益。

如何避免这种竞争条件:

CurrentThread

请注意,修复Observables组合的订阅的解决方案不是更改源observable的工作方式,而是确保您的订阅逻辑不允许存在任何竞争条件。这很重要,因为试图解决Observable中的这个问题只是改变行为,而不是修复竞争。如果我们改变了源并稍后将其切换出来,那么订阅逻辑仍然是错误的。

答案 1 :(得分:1)

我怀疑这是调度程序。这种变化导致两者的行为相同:

var x = Observable.Create<int>(o =>
    {
        NewThreadScheduler.Default.Schedule(() =>
        {
            for (int i = 1; i < 11; i++)
            {
                o.OnNext(i);
            }

            o.OnCompleted();
        });

        return Disposable.Create(() => Console.WriteLine("Disposed"));
    });

使用Scheduler.Immediate会产生与您相同的行为。