C# 为什么我用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

我正在努力扩展我在Rx方面的知识。因此,我只是在玩流,并试图让他们的行为,因为我希望他们做

虽然我读过bevor的文章,说Repeat()操作符在实践中有困难,因为您可能会在未完成和重新订阅之间丢失通知,但我自己无法理解为什么会发生以下情况

        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
由于时间上的细微差异,时间约为2时的两个动作可以按任意顺序进行。而且顺序很重要,要么在
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();