重播主题订阅行为

时间:2018-09-26 11:39:09

标签: system.reactive

以下代码按预期工作,但是当我取消注释'o.OnCompleted();'行时的行为方式令我感到困惑

该代码将所有订阅者加入单个长操作的结果,并将结果缓存给其他订阅者2秒钟。在这段时间之后的任何订阅将再次开始该过程。

订阅将来自其他线程(与线程池模拟)。

        var obs = Observable.Create((IObserver<Guid> o) =>
        {
            Console.WriteLine("Start");
            Thread.Sleep(1000); // process
            Console.WriteLine("End");
            o.OnNext(Guid.NewGuid());
            //o.OnCompleted(); // <-- uncomment this
            return Disposable.Empty;
        })
        .Replay(TimeSpan.FromSeconds(2))
        .RefCount()
        .Take(1);

        ThreadPool.QueueUserWorkItem(delegate
        {
            // simulate request from threadpool
            obs.Subscribe(x => Console.WriteLine($"1: {x}"), () => Console.WriteLine($"1: complete"));
        });

        ThreadPool.QueueUserWorkItem(delegate
        {
            obs.Subscribe(x => Console.WriteLine($"2: {x}"), () => Console.WriteLine($"2: complete"));
        });

        Thread.Sleep(4000);

        ThreadPool.QueueUserWorkItem(delegate
        {
            obs.Subscribe(x => Console.WriteLine($"3: {x}"), () => Console.WriteLine($"3: complete"));
        });

这是结果:

Start
End
1: 255BEFDC-2F14-40AD-AE77-2B005C5A3AA9
2: 255BEFDC-2F14-40AD-AE77-2B005C5A3AA9
1: complete
2: complete
Start
End
3: 1214DC63-F688-475A-9CB7-C3784054A4AC
3: complete

奇怪的行为是,如果我取消注释'o.OnCompleted()'行,结果将更改为:

Start
End
1: 255BEFDC-2F14-40AD-AE77-2B005C5A3AA9
2: 255BEFDC-2F14-40AD-AE77-2B005C5A3AA9
1: complete
2: complete
Start
End
3: complete

第3个订阅者导致可观察到根的另一个订阅,但结果丢失。看起来ReplaySubject缓存了先前可观察结果已完成的结果,但仍会导致新的订阅。这似乎是不直观的。我想了解为什么它不起作用。

注意:我最初使用Defer而不是Create进行了尝试,其结果与上面的第二次运行相同(出于明显的原因)。

1 个答案:

答案 0 :(得分:1)

使用Replay / RefCount对时,您将创建一个观察源,该观察源与源可观察对象具有共同的订阅。

来源:

  

返回一个可连接的可观察序列,该序列共享对基础序列的单个订阅,从而重放所有通知。

现在,重要的是要记住,一个可观察对象会产生一系列零个或多个值,然后是完整或错误信号。生成完整或错误后,它将无法生成值。

由于您共享源的公共订阅,并且如果您的源产生完整的订阅,则它不能产生更多的值。因此,当您致电o.OnCompleted()时,您正是在这样做。

此外,作为一个旁注,您应该避免在return Disposable.Empty;内写Create。这意味着您正在创建一个可观测的项目,该事件在订阅返回之前就完成了,这可能会导致竞争状况。

不编写代码的方式是:

var obs =
    Observable
        .Defer(() => Observable.Return(Guid.NewGuid()).Concat(Observable.Never<Guid>()))
        .Replay(TimeSpan.FromSeconds(2.0))
        .RefCount()
        .Take(1);

但这与不调用o.OnCompleted()相同。