【问题标题】:Limit replay buffer by observable通过 observable 限制重播缓冲区
【发布时间】:2014-12-06 06:43:06
【问题描述】:

我有一个包含实时数据的流,以及一个基本上划分属于一起的部分实时数据的流。现在,当有人订阅实时数据流时,我想重播他们的实时数据。但是我不想记住所有实时数据,只记住自上次另一个流发出值以来的部分。

There is an issue 这将解决我的问题,因为有一个重播运算符可以完全按照我的意愿(或至少我认为)。

目前有什么方法可以轻松做到这一点?有没有比以下更好的方法?

private class ReplayWithLimitObservable<TItem, TDelimiter> : IConnectableObservable<TItem>
{
    private readonly List<TItem> cached = new List<TItem>();
    private readonly IObservable<TDelimiter> delimitersObservable;
    private readonly IObservable<TItem> itemsObservable;
    public ReplayWithLimitObservable(IObservable<TItem> itemsObservable, IObservable<TDelimiter> delimitersObservable)
    {
        this.itemsObservable = itemsObservable;
        this.delimitersObservable = delimitersObservable;
    }

    public IDisposable Subscribe(IObserver<TItem> observer)
    {
        lock (cached)
        {
            cached.ForEach(observer.OnNext);
        }

        return itemsObservable.Subscribe(observer);
    }

    public IDisposable Connect()
    {
        var delimiters = delimitersObservable.Subscribe(
            p =>
                {
                    lock (cached)
                    {
                        cached.Clear();
                    }
                });
        var items = itemsObservable.Subscribe(
            p =>
                {
                    lock (cached)
                    {
                        cached.Add(p);
                    }
                });
        return Disposable.Create(
            () =>
                {
                    items.Dispose();
                    delimiters.Dispose();
                    lock (cached)
                    {
                        cached.Clear();
                    }
            });
}

public static IConnectableObservable<TItem> ReplayWithLimit<TItem, TDelimiter>(IObservable<TItem> items, IObservable<TDelimiter> delimiters)
{
    return new ReplayWithLimitObservable<TItem, TDelimiter>(items, delimiters);
}

【问题讨论】:

  • 只是一个想法...ConcurrentBag&lt;T&gt; 不是比在cached 上使用lock 更好的选择吗?我的意思是,这就是它的设计目的......
  • @toadflakz - AFAIK,ConcurrentBag 不保证保留添加顺序(如果可观察的项目行为正确,我会使用列表以正确的顺序获取项目)。 ConcurrentQueue 可以解决这个问题,但清除列表比清除 ConcurrentQueue 更容易。
  • 感谢您的解释 - 我对实时数据开发很感兴趣,因此非常感谢有经验的人提供的代码设计决策见解。
  • 我同意您的问题代表了框架中的核心限制,您提到的我的工作项目建议向Replay 添加“反应性/反应性”重载可以解决您的问题。
  • 在那之前,您的解决方案很好,除了几个问题:1) 永远不要自己实现IObservable&lt;T&gt;。在这种情况下,您需要一个IConnectableObservable&lt;T&gt;,它比实现自定义主题更快;但是,您仍然应该从ObservableBase&lt;T&gt; 派生,以确保自动分离行为、蹦床存在等。 2) 您的Subscribe 实现有一个竞争条件,可能会让您错过通知。确保在锁内调用itemsObservable.Subscribe

标签: c# system.reactive


【解决方案1】:

这是你想要的吗?它的优点是将所有锁定和竞争条件留给 Rx 专业人士:)

private class ReplayWithLimitObservable<T, TDelimiter> : IConnectableObservable<T>
{
  private IConnectableObservable<IObservable<T>> _source;

  public ReplayWithLimitObservable(IObservable<T> source, IObservable<TDelimiter> delimiter)
  {
    _source = source
      .Window(delimiter) // new replay window on delimiter
      .Select<IObservable<T>,IObservable<T>>(window =>
      {
        var replayWindow = window.Replay();

        // immediately connect and start memorizing values
        replayWindow.Connect();

        return replayWindow;
      })
      .Replay(1); // remember the latest window
  }

  IDisposable Connect()
  {
    return _source.Connect();
  }

  IDisposable Subscribe(IObserver<T> observer)
  {
    return _source
      .Concat()
      .Subscribe(observer);
  }
}

public static IConnectableObservable<TItem> ReplayWithLimit<TItem, TDelimiter>(IObservable<TItem> items, IObservable<TDelimiter> delimiters)
{
    return new ReplayWithLimitObservable<TItem, TDelimiter>(items, delimiters);
}

【讨论】:

  • 这样更好,但我仍然不喜欢必须实现IConnectableObservable&lt;T&gt; 的事实。那不应该是真的,你不觉得吗?您在这里非常接近避免它,但仍然如此......;-)
  • 至少,this,尽管ReplaySubject&lt;T&gt; 的“反应/反应”版本是最好的。它会在一行代码中解决这个问题:return xs.Multicast(new ReplaySubject&lt;T&gt;(ys));
  • @DaveSexton - 完全同意,我对这个问题的反应是 IConnectableObservable&lt;T&gt; 的工厂确实是 Rx 的一个受欢迎的补充。
  • Brandon 的回答很好,尤其是 Replay(1) 和延迟 Concat 的机械师 - 非常好。
  • 是的,我尝试使用窗口和重播来做到这一点,但我没有想到 Replay(1)。谢谢,这就是我想要的。
猜你喜欢
  • 1970-01-01
  • 2014-09-11
  • 2022-11-11
  • 1970-01-01
  • 2011-06-15
  • 2013-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多