Observable.Subscribe与多个IEnumerables一起使用时的行为

时间:2016-10-16 20:57:25

标签: c# system.reactive

我正在尝试从两个数组(IObservable<T> s)创建IEnumerable。我试图避免显式迭代数组并调用observer.OnNext。我遇到了Observable.Subscribe扩展方法,乍一看似乎是我需要的方法。但是,它没有像我预期的那样工作,我不知道为什么。

以下代码是一个示例:

  class Program
  {
    static void Main(string[] args)
    {
      var observable = Observable.Create<char>(observer =>
        {
          var firstBytes = new[] {'A'};
          var secondBytes = new[] {'Z', 'Y'};
          firstBytes.Subscribe(observer);
          secondBytes.Subscribe(observer);

          return Disposable.Empty;
        }
      );

      observable.Subscribe(b => Console.Write(b));
    }
  }

这是“AZ”的输出,而不是我预期的“AZY”。现在,如果我在secondBytes之前订阅firstBytes,则输出为“ZAY”!这似乎表明它正在逐步枚举两个数组 - 哪种解释了“AZ”输出。

无论如何,我完全不知道为什么它会像这样,并且会欣赏人们可能提供的任何洞察力。

2 个答案:

答案 0 :(得分:2)

因为您订阅了两个 observable,而不是单个observable,它是两个observable的串联,所以有两个可能的源可以调用观察者的OnComplete方法。由于第一个数组较短,因此在第一个项目发出后完成,观察者取消订阅,因为它已收到完成通知。

正确的方法是将两个序列组合成一个序列,然后订阅:

var observable = Observable.Create<char>(observer =>
{
    var firstBytes = new[] { 'A' };
    var secondBytes = new[] { 'Z', 'Y' };

    return firstBytes.Concat(secondBytes).Subscribe(observer);
});

observable.Subscribe(Console.Write);

答案 1 :(得分:2)

锁定步骤迭代行为的原因可以通过Observable.Subscribe(IEnumerable source)的实现来解释,"recursive" algorithm使用{{3}},它通过在调度程序操作中调用e.MoveNext来工作。如果成功,则发出该值,然后将新的调度程序操作排队以从可枚举中读取下一个值。

当您订阅两个枚举但没有为订阅指定任何特定的调度程序时,默认的迭代调度程序将用于这些操作(由SchedulerDefaults.Iteration定义),默认情况下在当前线程上运行。这意味着枚举操作将排队等待当前订阅操作完成后运行。这会导致枚举操作交错 - 类似这样

  1. firstBytes.Subscribe() - &gt; queue enumerate action
  2. secondBytes.Subscribe() - &gt; queue enumerate action
  3. 调用firstBytes.MoveNext() - &gt; OnNext(&#34; A&#34;) - &gt;排队下一次枚举行动
  4. 调用secondBytes.MoveNext() - &gt; OnNext(&#34; Z&#34;) - &gt;排队下一次枚举行动
  5. 调用firstBytes.MoveNext() - &gt; OnCompleted()
  6. 调用secondBytes.MoveNext() - &gt; OnNext(Y) - &gt;排队下一次枚举行动
  7. 调用secondBytes.MoveNext() - &gt; OnCompleted()
  8. 观察者在步骤5接收OnCompleted()通知,因此忽略剩余的secondBytes枚举步骤。如果您已经退回订购的一次性用品,则第二次枚举将在此时被取消。