以下代码按预期工作,但是当我取消注释'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进行了尝试,其结果与上面的第二次运行相同(出于明显的原因)。
答案 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()
相同。