我有以下的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有什么区别?
感谢。
答案 0 :(得分:3)
虽然我没有耐心对Rx.NET源代码进行排序,以确切了解哪些实现细节会导致这种确切的行为,但我可以提供以下见解:
您所看到的行为差异是由竞争条件引起的。在这种情况下,竞争者是a
和b
的订阅,这是因为您订阅Observable.Merge
返回的可观察量而发生的。您订阅了c
,后者又订阅了a
和b
。 a
和b
根据Publish
或RefCount
的{{1}}和x
来定义,具体取决于您选择的是哪种情况。< / p>
在这种情况下,您使用的是自定义Observable。订阅时,您的自定义可观察 和同步开始r
数字1到10,然后调用onNext
。有趣的是,此订阅是由onCompleted
Observable在第一次订阅 时引起的。它是Publish().RefCount()
第一次订阅,因为a
是a
的第一个参数。因此,在Merge
订阅Merge
之前,您的订阅已经完成。 b
订阅Merge
,即b
可观察对象。该observable已经完成,因此RefCount
查找要合并的下一个Observable。由于没有更多的Observables要合并,并且因为所有现有的Observable都已完成,所以合并的observable完成了。
通过你的自定义observable接下来的值已经遍历了“对”可观察对象,而不是“均匀”可观察对象。因此,您最终得到以下结果:
Merge
在这种情况下,您使用内置的// "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
会产生与您相同的行为。