Поддерживают ли реактивные расширения скользящие буферы? - PullRequest
22 голосов
/ 29 сентября 2011

Я использую реактивные расширения для сортировки данных в буферы по 100 мс:

this.subscription = this.dataService
    .Where(x => !string.Equals("FOO", x.Key.Source))
    .Buffer(TimeSpan.FromMilliseconds(100))
    .ObserveOn(this.dispatcherService)
    .Where(x => x.Count != 0)
    .Subscribe(this.OnBufferReceived);

Это прекрасно работает.Однако я хочу, чтобы поведение немного отличалось от того, которое обеспечивается операцией Buffer.По сути, я хочу сбросить таймер, если получен другой элемент данных.Только когда данные не были получены за все 100 мс, я хочу обрабатывать их.Это открывает возможность никогда обработки данных, поэтому я также должен иметь возможность указать максимальное количество.Я мог бы представить что-то вроде:

.SlidingBuffer(TimeSpan.FromMilliseconds(100), 10000)

Я осмотрелся вокруг и не смог найти ничего подобного в Rx?Кто-нибудь может подтвердить / опровергнуть это?

Ответы [ 4 ]

15 голосов
/ 20 марта 2012

Это возможно путем комбинирования встроенных Window и Throttle методов Observable.Во-первых, давайте решим более простую проблему, где мы игнорируем условие максимального счета:

public static IObservable<IList<T>> BufferUntilInactive<T>(this IObservable<T> stream, TimeSpan delay)
{
    var closes = stream.Throttle(delay);
    return stream.Window(() => closes).SelectMany(window => window.ToList());
}

Мощный Window метод сделал тяжелую работу.Теперь достаточно просто увидеть, как добавить максимальное количество:

public static IObservable<IList<T>> BufferUntilInactive<T>(this IObservable<T> stream, TimeSpan delay, Int32? max=null)
{
    var closes = stream.Throttle(delay);
    if (max != null)
    {
        var overflows = stream.Where((x,index) => index+1>=max);
        closes = closes.Merge(overflows);
    }
    return stream.Window(() => closes).SelectMany(window => window.ToList());
}

Я напишу сообщение, объясняющее это в моем блоге.https://gist.github.com/2244036

Документация для метода Window:

14 голосов
/ 30 сентября 2011

Я написал расширение, чтобы сделать большую часть того, что вам нужно - BufferWithInactivity.

Вот оно:

public static IObservable<IEnumerable<T>> BufferWithInactivity<T>(
    this IObservable<T> source,
    TimeSpan inactivity,
    int maximumBufferSize)
{
    return Observable.Create<IEnumerable<T>>(o =>
    {
        var gate = new object();
        var buffer = new List<T>();
        var mutable = new SerialDisposable();
        var subscription = (IDisposable)null;
        var scheduler = Scheduler.ThreadPool;

        Action dump = () =>
        {
            var bts = buffer.ToArray();
            buffer = new List<T>();
            if (o != null)
            {
                o.OnNext(bts);
            }
        };

        Action dispose = () =>
        {
            if (subscription != null)
            {
                subscription.Dispose();
            }
            mutable.Dispose();
        };

        Action<Action<IObserver<IEnumerable<T>>>> onErrorOrCompleted =
            onAction =>
            {
                lock (gate)
                {
                    dispose();
                    dump();
                    if (o != null)
                    {
                        onAction(o);
                    }
                }
            };

        Action<Exception> onError = ex =>
            onErrorOrCompleted(x => x.OnError(ex));

        Action onCompleted = () => onErrorOrCompleted(x => x.OnCompleted());

        Action<T> onNext = t =>
        {
            lock (gate)
            {
                buffer.Add(t);
                if (buffer.Count == maximumBufferSize)
                {
                    dump();
                    mutable.Disposable = Disposable.Empty;
                }
                else
                {
                    mutable.Disposable = scheduler.Schedule(inactivity, () =>
                    {
                        lock (gate)
                        {
                            dump();
                        }
                    });
                }
            }
        };

        subscription =
            source
                .ObserveOn(scheduler)
                .Subscribe(onNext, onError, onCompleted);

        return () =>
        {
            lock (gate)
            {
                o = null;
                dispose();
            }
        };
    });
}
3 голосов
/ 19 марта 2015

С помощью Rx Extensions 2.0 вы можете удовлетворить оба требования новой перегрузкой буфера, принимая время ожидания и размер:

this.subscription = this.dataService
    .Where(x => !string.Equals("FOO", x.Key.Source))
    .Buffer(TimeSpan.FromMilliseconds(100), 1)
    .ObserveOn(this.dispatcherService)
    .Where(x => x.Count != 0)
    .Subscribe(this.OnBufferReceived);

См. https://msdn.microsoft.com/en-us/library/hh229200(v=vs.103).aspx для документации.

0 голосов
/ 01 октября 2011

Полагаю, это может быть реализовано поверх метода Buffer, как показано ниже:

public static IObservable<IList<T>> SlidingBuffer<T>(this IObservable<T> obs, TimeSpan span, int max)
        {
            return Observable.CreateWithDisposable<IList<T>>(cl =>
            {
                var acc = new List<T>();
                return obs.Buffer(span)
                        .Subscribe(next =>
                        {
                            if (next.Count == 0) //no activity in time span
                            {
                                cl.OnNext(acc);
                                acc.Clear();
                            }
                            else
                            {
                                acc.AddRange(next);
                                if (acc.Count >= max) //max items collected
                                {
                                    cl.OnNext(acc);
                                    acc.Clear();
                                }
                            }
                        }, err => cl.OnError(err), () => { cl.OnNext(acc); cl.OnCompleted(); });
            });
        }

ПРИМЕЧАНИЕ. Я не проверял его, но надеюсь, что он дает вам идею.

...