这种可观察性会变热吗?

时间:2016-07-22 13:41:15

标签: c# system.reactive

这不会让观察到热吗?

using System;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;

namespace ObservableNumberGenerator.ObservableImplementationReliesOnOperator.Hot
{
    public class RandomNumbers : IObservable<int>, IDisposable
    {
        protected Random _random = null;
        protected int _maxNumbersToGenerate;
        protected int _startAfterMilliseconds = 1000;
        protected int _generateEveryMilliseconds = 1000;

        protected IObservable<int> _innerObservable = null;
        protected IDisposable _innerSubscription = null;
        protected bool _completed = false;
        private bool disposedValue = false;


        public RandomNumbers(int maxNumbersToGenerate,
            int startAfterMilliseconds = 1000, 
            int generateEveryMilliseconds = 1000)
        {
            _maxNumbersToGenerate = maxNumbersToGenerate;
            _startAfterMilliseconds = startAfterMilliseconds;
            _generateEveryMilliseconds = generateEveryMilliseconds;

            _random = new Random();

            _innerObservable = Observable.Timer(
                TimeSpan.FromMilliseconds(_startAfterMilliseconds),
                TimeSpan.FromMilliseconds(_generateEveryMilliseconds))
                .Select(v => GenerateNumber())
                .Take(_maxNumbersToGenerate);

            _innerSubscription = _innerObservable.Subscribe(OnNext, OnError, OnCompleted);
        }

        protected virtual void OnCompleted()
        {
            _completed = true;
        }

        protected virtual void OnError(Exception ex)
        {
        }

        protected virtual void OnNext(int value)
        {
        }

        protected virtual int GenerateNumber()
        {
            return _random.Next();
        }

        public IDisposable Subscribe(IObserver<int> observer)
        {
            if (observer == null) throw new ArgumentNullException("observer");

            if (_completed)
            {
                observer.OnCompleted();

                return Disposable.Empty;
            }
            else
            {
                return _innerObservable.Subscribe(observer);
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    _innerSubscription.Dispose();
                }

                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}

或者我 PublishConnect以使其变热?

public class RandomNumbers : IObservable<int>, IDisposable
{
    protected IConnectableObservable<int> _innerObservable = null;        

    public RandomNumbers(int maxNumbersToGenerate,
        int startAfterMilliseconds = 1000, 
        int generateEveryMilliseconds = 1000)
    {
        _maxNumbersToGenerate = maxNumbersToGenerate;
        _startAfterMilliseconds = startAfterMilliseconds;
        _generateEveryMilliseconds = generateEveryMilliseconds;

        _random = new Random();

        _innerObservable = Observable
            .Timer(TimeSpan.FromMilliseconds(_startAfterMilliseconds),
            TimeSpan.FromMilliseconds(_generateEveryMilliseconds))
            .Select(v => GenerateNumber())
            .Take(_maxNumbersToGenerate)
            .Publish();

        _innerObservable.Connect();

        _innerSubscription = _innerObservable.Subscribe(OnNext, OnError, OnCompleted);
    }

    ...
}

2 个答案:

答案 0 :(得分:2)

你的第一个代码是冷可观察的。这里有一些简单的客户端代码来演示:

    static void Main(string[] args)
    {
        var x = new RandomNumbers(10, 0, 500);
        x.Timestamp().Subscribe(i => Console.WriteLine("First sub: {0} {1}", i.Timestamp.DateTime.ToString("O"), i.Value));
        Thread.Sleep(1000);
        x.Timestamp().Subscribe(i => Console.WriteLine("Second sub: {0} {1}", i.Timestamp.DateTime.ToString("O"), i.Value));
        Console.Read();
    }

使用冷可观察,您会看到“第一个子”行与“第二个子”行不匹配。随着热门实施,他们做到了。您还会注意到冷实现,“First sub”和“Second sub”行的数量相同。通过热门实施,全球最多可以观察到10个。

只是为了澄清热与冷。假设有一个静态/稳定的数据流,您将获得此大理石图,显示订阅t0t1时订阅的不同之处:

hot source:  A---B---C---D---
sub at t0 :  A---B---C---D---
sub at t1 :      B---C---D---

cold source: A---B---C---D---
sub at t0  : A---B---C---D---
sub at t1  :     A---B---C---

在我们的例子中,我们有一个随机流:

hot source:  R()-R()-R()-R()-
sub at t0 :  R1--R2--R3--R4--
sub at t1 :      R2--R3--R4--

cold source: R()-R()-R()-R()-
sub at t0  : R1--R2--R3--R4--
sub at t1  :     R5--R6--R7--

Random()调用的结果在热的可观察量中共享,而在寒冷中他们没有。如果您通过索引进行度量,则热可观察订阅会获得不同的值,但它们会在给定时间t收到相同的值。在稳定的源中,冷可观察订阅按索引匹配,但它们不随时间匹配。在不稳定的来源中,就像我们的情况一样,冷可观察量与指数或时间不匹配。

答案 1 :(得分:-1)

正如Enigmativity指出的那样,正如Shlomo的回答似乎也暗示的那样,仅仅因为你在构造函数中订阅了observable就不会让它“共享”,因此想要让它变得热门。

我认真考虑为什么应该这样,然后我查看了Subscribe运算符上的Timer方法的源代码,并实现了我之前已经知道的但已经忘记的东西,因为我看起来也是硬。

大多数运算符都是以这种方式实现的:每次在Subscribe运算符上调用Timer方法时,都会创建一个内部类的新实例,该实例在默认池调度程序上启动新序列。

所以,在Timer类的某个地方写的是:

namespace System.Reactive.Linq.ObservableImpl
{
    internal class Timer : Producer<long>
    {
        protected override IDisposable Run(
                               IObserver<long> observer, 
                               IDisposable cancel, 
                               Action<IDisposable> setSink)
        {
            if (this._period.HasValue)
            {
                Timer.TimerImpl timerImpl = 
                    new Timer.TimerImpl(this, observer, cancel);

                setSink(timerImpl);
                return timerImpl.Run();
            }

            ...
        }
    }
}

将这个事实与我的自定义观察的Subscribe方法中的事实相结合,我这样做:

public IDisposable Subscribe(IObserver<int> observer)
{
    if (_completed)
    {
        ...
    }
    else
    {
        return _innerObservable.Subscribe(observer);
    }
}

如果我通过像这样的内部观察者引导所有观察,我可以在不使用PublishConnectRefCount运算符的情况下使这个可观察到的热度:

using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;

namespace ObservableNumberGenerator.ObservableImplementationReliesOnOperator.Hot
{
    public class RandomNumbers : IObservable<int>, IDisposable
    {
        protected Random _random = null;
        protected int _maxNumbersToGenerate;
        protected int _startAfterMilliseconds = 1000;
        protected int _generateEveryMilliseconds = 1000;

        protected List<IObserver<int>> _observers = 
                            new List<IObserver<int>>();

        protected IObservable<int> _innerObservable = null;
        protected IDisposable _innerSubscription = null;
        protected bool _completed = false;
        private bool disposedValue = false;

        public RandomNumbers(int maxNumbersToGenerate,
            int startAfterMilliseconds = 1000, int generateEveryMilliseconds = 1000)
        {
            _maxNumbersToGenerate = maxNumbersToGenerate;
            _startAfterMilliseconds = startAfterMilliseconds;
            _generateEveryMilliseconds = generateEveryMilliseconds;

            _random = new Random();

            _innerObservable = Observable
                .Timer(
                  TimeSpan.FromMilliseconds(_startAfterMilliseconds),
                  TimeSpan.FromMilliseconds(_generateEveryMilliseconds))
                .Select(v => GenerateNumber())
                .Take(_maxNumbersToGenerate);

            _innerSubscription = 
                _innerObservable.Subscribe(OnNext, OnError, OnCompleted);
        }

        protected virtual void OnCompleted()
        {
            _completed = true;

            foreach (var observer in _observers)
                observer.OnCompleted();
        }

        protected virtual void OnError(Exception ex)
        {
            _completed = true;

            foreach (var observer in _observers)
                observer.OnError(ex);
        }

        protected virtual void OnNext(int value)
        {
            foreach (var observer in _observers)
                observer.OnNext(value);
        }

        protected virtual int GenerateNumber()
        {
            return _random.Next();
        }

        public IDisposable Subscribe(IObserver<int> observer)
        {
            if (observer == null) throw new ArgumentNullException("observer");

            _observers.Add(observer);

            if (_completed)
            {
                observer.OnCompleted();
            }

            return Disposable.Empty;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    _innerSubscription.Dispose();
                }

                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}

请注意任何想要这样做的人:

我所有的例子都只是练习,他们的目的是教育自己关于Rx的内部运作。

正如Enigmativity正确地指出的那样,不能在生产代码中以这种方式实现任何可观察的。

首先,我List<IObserver<T>>_innerObserver中的观察者都不是安全的观察者。在Rx源代码中,它们将我们传递给的每个观察者转换为一个安全的观察者,在每个try/finallyOnNextOnError实现中都有OnCompleted个子句,以防范异常。有关详细信息,请查看System.Reactive.SafeObserver<TSource>类和System.Reactive.AnonymousObserver<T>类的源代码。