c# – Rx IObservable缓冲来平滑突发事件

我有一个可观测的序列,可以在快速突发中产生事件(即:一个接一个地发生五个事件,然后是长时间延迟,另一个快速突发事件等)。我想通过在事件之间插入短暂的延迟来平滑这些突发。以下图为例:

Raw:      --oooo--------------ooooo-----oo----------------ooo|
Buffered: --o--o--o--o--------o--o--o--o--o--o--o---------o--o--o|

我目前的方法是通过Observable.Interval()生成一个类似节拍器的定时器,它可以从原始流中拉出另一个事件。问题是,我无法弄清楚如何将这个计时器与我原始的无缓冲的可观察序列相结合。

IObservable.Zip()接近做我想要的,但它只有原始流生成比定时器更快的事件才有效。一旦原始流中存在明显的延迟,计时器会建立一系列不必要的事件,然后立即与原始流中的下一个事件突发配对。

理想情况下,我想要一个具有以下功能签名的IObservable扩展方法,该方法生成了上述概述。现在,来救我StackOverflow 🙂

public static IObservable<T> Buffered(this IObservable<T> src, TimeSpan minDelay)

PS。我是Rx的全新,所以我很抱歉,如果这是一个简单的问题…

简单但有缺陷的方法

这是我初始的天真和简单的解决方案,有很多问题:

public static IObservable<T> Buffered<T>(this IObservable<T> source, TimeSpan minDelay)
{
    Queue<T> q = new Queue<T>();
    source.Subscribe(x => q.Enqueue(x));
    return Observable.Interval(minDelay).Where(_ => q.Count > 0).Select(_ => q.Dequeue());
}

第一个明显的问题是,内部订阅返回的原始源的IDisposable将丢失,因此订阅不能终止。通过此方法返回的IDisposable上的Calling Dispose会杀死定时器,而不是现在不必要地填充队列的基本raw事件Feed,而没有人可以从队列中提取事件。

第二个问题是,从原始事件流到缓冲流,无法将异常或流结束通知传播,在订阅原始源时,它们将被忽略。

最后但并非最不重要的是,现在我有一个代码可以定期唤醒,无论实际上是否有任何工作要做,我宁愿在这个美好的新的反应性世界中避免。

方式过于复杂的appoach

为了解决我的初始简单方法中遇到的问题,我写了一个更复杂的函数,其行为非常像IObservable.Delay()(我使用.NET Reflector来读取该代码并将其用作我的函数的基础)。不幸的是,很多样板逻辑(如AnonymousObservable)在system.reactive代码之外是不可公开访问的,所以我不得不复制并粘贴很多代码。这个解决方案似乎是有效的,但由于其复杂性,我对自己的错误没有自信。

我不能相信没有办法使用标准的Reactive扩展的一些组合来完成此任务。我讨厌感觉像我不必要地重塑轮子,而我正在努力打造的图案似乎是一个相当标准的。

这实际上是A way to push buffered events in even intervals的重复,但我将在这里包括一个摘要(原来看起来很混乱,因为它看起来几个选择)。

public static IObservable<T> Buffered<T>(this IObservable<T> source, TimeSpan minDelay)
{
    return source.Drain(x => 
        Observable.Empty<int>()
            .Delay(minDelay)
            .StartWith(x)
    );
}

我的执行排水工作像SelectMany,除了等待先前的输出完成(你可以将它认为是ConactMany,而SelectMany更像是MergeMany)。内置的排水系统不会这样工作,因此您需要包含以下实现:

public static class ObservableDrainExtensions
{
    public static IObservable<TOut> Drain<TSource, TOut>(
        this IObservable<TSource> source, 
        Func<TSource, IObservable<TOut>> selector)
    {
        return Observable.Defer(() =>
        {
            BehaviorSubject<Unit> queue = new BehaviorSubject<Unit>(new Unit());

            return source
                .Zip(queue, (v, q) => v)
                .SelectMany(v => selector(v)
                    .Do(_ => { }, () => queue.OnNext(new Unit()))
                );
        });
    }
}
http://stackoverflow.com/questions/4505529/rx-iobservable-buffering-to-smooth-out-bursts-of-events

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – Rx IObservable缓冲来平滑突发事件