簡體   English   中英

Observable.Interval重新啟動每個訂閱

[英]Observable.Interval restarts for each subscription

我第一次定義測量時間表的嘗試是:

    var schedule = Observable.Concat(
                Observable.Interval(TimeSpan.FromSeconds(1)).Take(3),
                Observable.Interval(TimeSpan.FromSeconds(3)).Take(3),
                Observable.Interval(TimeSpan.FromSeconds(5)));

不幸的是,當取消訂閱和重新訂閱時,它會重新啟動,這對我而言不是理想的行為。 因此,我想到了這樣的東西:

    class Schedule : IObservable<DateTime>, IDisposable
    {
        readonly ISubject<DateTime> _subject;
        readonly IDisposable _subscrption;

        public Schedule()
        {
            _subject = new BehaviorSubject<DateTime>(DateTime.UtcNow);
            _subscrption = Observable.Concat(
                Observable.Interval(TimeSpan.FromSeconds(1)).Take(3),
                Observable.Interval(TimeSpan.FromSeconds(3)).Take(3),
                Observable.Interval(TimeSpan.FromSeconds(5)))
                .Select(i => DateTime.UtcNow)
                .Subscribe(_subject);
        }

        public IDisposable Subscribe(IObserver<DateTime> observer)
        {
            return _subject.Subscribe(observer);
        }

        public void Dispose()
        {
            _subscrption.Dispose(); 
        }
    }

它可以工作,但需要在使用后進行處理。 有什么簡單的方法可以定義Schedule而不暴露IDisposable?

如果您要取消所有觀察者的訂閱,然后重新訂閱,則真的沒有辦法讓Disposable保持跟蹤連接。 因為您的Observable不會直接知道最新的取消訂閱是否真的是最后一個。

我建議使用“ Generate而不是“連接可觀察對象”

IConnectableObservable<DateTime> source = Observable.Generate(
  0,
  _ => true,
  x => x + 1,
  x => DateTime.UtcNow,
  x => {
    if (x < 3) return TimeSpan.FromSeconds(1);
    else if (x < 5) return TimeSpan.FromSeconds(3);
    else return TimeSpan.FromSeconds(5);
}).Publish();

Publish將您的Observable變為ConnectableObservable,並為您提供兩個選項來管理源何時實際產生事件。

//1) Explicit connect
IDisposable connection = source.Connect();

//2) RefCounted connection
IObservable<DateTime> rcSource = source.RefCount();

在第一個版本中,一旦連接,源將變為“熱”狀態;當您要斷開連接時,應丟棄連接。

在第二個中,當沒有更多觀察者時,源將自動斷開連接。

另請參閱熱與冷可觀察物

這是我定義的不需要處理的解決方案。 不過,這非常復雜,因此,如果可以使用Rx工具簡化某些操作,我將不勝感激。

用法:

  IObservable<DateTime> schedule = new Schedule()
            .Setup(TimeSpan.FromSeconds(1), 3)
            .Setup(TimeSpan.FromSeconds(3), 3)
            .Setup(TimeSpan.FromSeconds(5));

時間表在哪里:

    class Schedule : IObservable<DateTime>
    {
        readonly TimeSpan TimerPrecision = TimeSpan.FromMilliseconds(1);
        readonly IEnumerable<TimeSpan> Intervals;
        readonly IEnumerator<DateTime> Event;

        public Schedule()
            : this(new TimeSpan[0])
        {
        }

        Schedule(IEnumerable<TimeSpan> intervals)
        {
            Intervals = intervals;
            Event = Start();
            Event.MoveNext();
        }

        public Schedule Setup(TimeSpan interval)
        {
            return Setup(interval, Int32.MaxValue);
        }

        public Schedule Setup(TimeSpan interval, int repeat)
        {
            return new Schedule(
                Intervals.Concat(
                    Enumerable.Repeat(interval, repeat)));
        }

        public IDisposable Subscribe(IObserver<DateTime> observer)
        {
            var timer = new System.Timers.Timer() { AutoReset = true };
            timer.Elapsed += (s, a) => 
            {                                                
                observer.OnNext(DateTime.UtcNow);
                if (!TryArm(timer))
                    observer.OnCompleted();
            };

            if (!TryArm(timer))
                observer.OnCompleted();

            return timer;
        }

        IEnumerator<DateTime> Start()
        {
            var time = DateTime.UtcNow;
            yield return time;

            foreach (var interval in Intervals)
            {
                time += interval;
                yield return time;
            }
        }

        TimeSpan Delay()
        {
            var now = DateTime.UtcNow;
            lock (Event)
                while (Event.Current - DateTime.UtcNow < TimerPrecision)
                    Event.MoveNext();

            return Event.Current - now;
        }

        bool TryArm(System.Timers.Timer timer)
        {
            try
            {
                timer.Interval = Delay().TotalMilliseconds;
                timer.Start();
                return true;
            }            
            catch(ObjectDisposedException)   
            {
                return false;
            }
            catch(InvalidOperationException)   
            {
                return false;
            }
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM