我正在努力扩展我对Rx的了解。所以我只是在玩溪流,试着让它们像我期望的那样表现。
虽然我已经读过bevor,但是Repeat()操作符在实践中有困难,因为你可能会在OnCompleted和重新订阅之间丢失通知,我无法弄清楚为什么会发生以下情况。
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();
当我运行这个例子时,结果完全不确定:
结果1:
1 at 1006
3 at 3007
5 at 4995
它遗漏了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();
答案 0 :(得分:1)
Windows(和其他桌面操作系统)不是运行时操作系统,所以你不能相信它的定时器精确到毫秒。特别是如果你有更多的计时器,这可能导致非确定性行为,这正是你的情况。
这就是原始序列的工作原理:
Take(1)
订阅了subject
subject
,写出1;在此之后,没有人再订阅subject
Take(1)
再次订阅subject
,另一个计时器用于延迟空的可观察开始2
被添加到subject
由于时间上的细微差别,这两个动作的时间约为2可以按任何顺序发生。并且顺序很重要,在Take()
重新订阅之前或之前添加2。因此,2可以写出或不写出。
如果您想要的是这样的序列:
然后我认为编辑中的代码是正确的。
但这绝不能保证确定性结果。在我的计算机上,如果我将延迟的空可观察量的等待时间更改为1960毫秒,则在使用Concat()
时会得到不确定的结果。
答案 1 :(得分:0)
只是为了晚会加入我的2c;如果您正在寻找测试的确定性,那么您应该使用TestScheduler而不是真正的ThreadPool / TaskPool / NewThread调度程序。纯粹是因为Svick指出的原因(操作系统调度摇摆不定)。