限制IObservable导致观察者不被调用

时间:2014-02-26 23:43:13

标签: c# system.reactive

我有这个简单的代码,其中:

  • 创建IObservable
  • 取样半秒
  • 使用ThreadPool调度程序
  • 订阅它
  • 使用SynchronizationContext
  • 观察它

以下是代码:

private void DisplayPoints()
{
    var x = 0;
    var ob = this.GeneratePoints();
    ob
      .Sample(TimeSpan.FromMilliseconds(500))
      .SubscribeOn(ThreadPoolScheduler.Instance)
      .ObserveOn(SynchronizationContext.Current)
      .Subscribe(d => Console.WriteLine(d));
}

private IObservable<double> GeneratePoints()
{
    return Observable.Create<double>(o => this.GeneratePoints(o));
}

private IDisposable GeneratePoints(IObserver<double> observer)
{
    var i = 0;
    while (true)
    {
        var value = random.Next(0, 100) * (1 / (double)random.Next(1, Math.Min(50, Math.Max(i, 1))));

        observer.OnNext(value);

        i++;
    }

    return Disposable.Empty;
}

但是,控制台上没有输出任何内容(即永远不会调用匿名观察者)。如果我删除Sample运算符,则会调用observer,尽管这种行为显然不是预期的(UI线程会被轰炸)。

我显然在这里遗漏了一些东西。我的目的是生成数据,通过IObserver推送数据,并通过UI显示一些数据。

编辑,因为有些人误解了我的意图(尽管上面已明确说过),我应该重申,我正在努力做的事情:

  • 使用算法生成一些数据(double值似乎足以解决我的问题)
  • 在GUI中显示数据

使用IObservable和Reactive Extensions似乎是解决我问题的好方法。

重复: 不会在实际代码中返回随机数 - 这只是占位符让我的预期行为发挥作用。

3 个答案:

答案 0 :(得分:1)

我怀疑您的问题与Throttle在内部通过DefaultScheduler.Instance引入并发并且IDisposable GeneratePoints(IObserver<double> observer)的实现是非标准的事实有关。

尝试像这样重新实现IObservable<double> GeneratePoints()

private IObservable<double> GeneratePoints()
{
    return Observable.Generate<int, double>(
        0,
        i => true,
        i => i + 1,
        i => random.Next(0, 100) * (1 / (double)random.Next(1, Math.Min(50, Math.Max(i, 1)))));
}

这可能会有所帮助。

问题来自您的observable在订阅过程中直接推出值。在创建observable时,应始终尝试使用标准的内置运算符。上面的代码使用内置的Generate运算符,因此它应该可以更好地使用您的代码。

答案 1 :(得分:1)

您可能不希望在紧密循环中生成随机数。最好使用时间间隔。下面每200毫秒产生一次随机数。

IObservable<double> observable =
     Observable.Interval(TimeSpan.FromMillSeconds(200))
          .Select((t,i) => random.Next(0, 100) 
                      * (1 / (double)random.Next(1, Math.Min(50, Math.Max(i, 1)))))

Enigmativity为您编写的代码也是有效的紧密循环。他对你在订阅过程中推出值的错误提出的观点也是正确的。您必须对代码进行的最小更改才能使其正常工作。

    private static Task GeneratePoints(IObserver<double> observer, CancellationToken token)
    {
        return Task.Run(() =>
        {
            var i = 0;
            var random = new Random();
            while ( true )
            {
                token.ThrowIfCancellationRequested();

                var value = random.Next(0, 100) * ( 1 / ( double ) random.Next(1, Math.Min(50, Math.Max(i, 1))) );

                observer.OnNext(value);

                i++;
            }
        });
    }

稍后

    Observable.Create<double>((observer, token) => GeneratePoints(observer, token));

请注意要传递的取消令牌。当序列的订阅者取消订阅时,将设置此令牌并且循环将终止。

然而,这是一项很多工作,而且Enigmativities的答案更简单,并为您提取上述代码。了解如何针对更复杂的情况手动执行此操作仍然很有用。

答案 2 :(得分:0)

Throttle只会在存在至少500毫秒(在您的情况下)的间隙时通过值。由于GeneratePoints推动价值远远超过此速度,所以不会发生任何事情。 Sample可能是您想要的运算符,在这种情况下,它将每500毫秒生成一个值。

Source:       1111111111111111111----------111---111111
Throttle (5): -----------------------1-----------------
Sample (5):   ----1----1----1----1---1------------1----1