C# 为什么我用Repeat()得到不确定的结果
我正在努力扩展我在Rx方面的知识。因此,我只是在玩流,并试图让他们的行为,因为我希望他们做 虽然我读过bevor的文章,说Repeat()操作符在实践中有困难,因为您可能会在未完成和重新订阅之间丢失通知,但我自己无法理解为什么会发生以下情况C# 为什么我用Repeat()得到不确定的结果,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,我正在努力扩展我在Rx方面的知识。因此,我只是在玩流,并试图让他们的行为,因为我希望他们做 虽然我读过bevor的文章,说Repeat()操作符在实践中有困难,因为您可能会在未完成和重新订阅之间丢失通知,但我自己无法理解为什么会发生以下情况 var subject = new Subject<string>(); var my = subject .Take(1) .Merge(Observable.E
var subject = new Subject<string>();
var my = subject
.Take(1)
.Merge(Observable.Empty<string>().Delay(TimeSpan.FromMilliseconds(2000)))
.Repeat();
my.Subscribe(Console.WriteLine);
var stopwatch = new Stopwatch();
stopwatch.Start();
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(1), () => subject.OnNext("1 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(2), () => subject.OnNext("2 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(3), () => subject.OnNext("3 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(4), () => subject.OnNext("4 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(5), () => subject.OnNext("5 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(6), () => subject.OnNext("6 at " + stopwatch.ElapsedMilliseconds));
Console.ReadLine();
这是好的,它遗漏了2和4,但即使在这个结果里面也有一些奇怪,因为实际上3和5之间没有真正的2秒差距
然而,结果可能更糟。看看这个:
1 at 1003
2 at 2003
4 at 4005
6 at 6004
1和2之间没有2秒的间隔。正好是一秒钟。他为什么不把它漏掉
如果有人能为我澄清事情,我会非常高兴
编辑
我只是注意到这里的合并可能是错误的。如果我将查询重构为Concat,那么事情似乎会发生:
var my = subject
.Take(1)
.Concat(Observable.Empty<string>().Delay(TimeSpan.FromMilliseconds(2000)))
.Repeat();
var my=subject
.采取(1)
.Concat(Observable.Empty().Delay(TimeSpan.From毫秒(2000)))
.重复();
Windows(和其他桌面操作系统)不是运行时操作系统,因此不能指望它的计时器精确到毫秒。特别是如果你有更多的定时器,这可能会导致非确定性行为,这正是你的情况
这就是您的原始序列的工作方式:
- 时间~0
订阅Take(1)
subject
- 延迟空可观测值的计时器开始滴答作响
- 时间~1
- 在
主题中添加1,写出1;在此之后,再也没有人订阅
subject
- 在
- 时间~2
- 延迟的空可观察对象的计时器超时。因此,
再次订阅Take(1)
,另一个用于延迟空可观测的计时器启动subject
- 大约同时,
被添加到2
subject
- 延迟的空可观察对象的计时器超时。因此,
Take()之前添加2,要么在重新订阅之前添加2。因此,2可以写出来,也可以不写
如果您想要的是这样的序列:
等待第一个项目并返回它
等大约两秒钟
等待第二个项目并返回它(忽略在两秒钟等待期间添加的任何项目)
那么我认为你编辑的代码是正确的
但这并不能保证确定性结果。在我的计算机上,如果我将延迟的空观测值的等待时间更改为1960 ms,当使用Concat()
将我的2秒延迟添加到聚会中时,我会得到不确定的结果;如果您正在为测试寻找确定性,那么应该使用TestScheduler,而不是真正的ThreadPool/TaskPool/NewThread Scheduler。纯粹是因为Svick指出的原因(操作系统调度会让事情变得更糟)
var my = subject
.Take(1)
.Concat(Observable.Empty<string>().Delay(TimeSpan.FromMilliseconds(2000)))
.Repeat();