Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Rx IObservable缓冲以平滑突发事件_C#_Buffering_System.reactive - Fatal编程技术网

C# Rx IObservable缓冲以平滑突发事件

C# Rx IObservable缓冲以平滑突发事件,c#,buffering,system.reactive,C#,Buffering,System.reactive,我有一个可观察的序列,它以快速爆发的方式产生事件(即:五个事件一个接一个,然后是一个长延迟,然后是另一个快速爆发的事件,等等)。我想通过在事件之间插入一个短暂的延迟来消除这些突发事件。以下图为例: Raw: --oooo--------------ooooo-----oo----------------ooo| Buffered: --o--o--o--o--------o--o--o--o--o--o--o---------o--o--o| 另外,我是Rx的新手,如果这是一个非常简单

我有一个可观察的序列,它以快速爆发的方式产生事件(即:五个事件一个接一个,然后是一个长延迟,然后是另一个快速爆发的事件,等等)。我想通过在事件之间插入一个短暂的延迟来消除这些突发事件。以下图为例:

Raw: --oooo--------------ooooo-----oo----------------ooo| Buffered: --o--o--o--o--------o--o--o--o--o--o--o---------o--o--o| 另外,我是Rx的新手,如果这是一个非常简单的问题,我深表歉意


1.简单但有缺陷的方法 以下是我最初的天真和简单的解决方案,其中有很多问题:

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());
}
publicstaticiobservable缓冲(这个IObservable源,TimeSpan-minDelay)
{
队列q=新队列();
订阅(x=>q.Enqueue(x));
返回可观察的.Interval(minDelay).Where(=>q.Count>0)。选择(=>q.Dequeue());
}
第一个明显的问题是,内部订阅返回给原始源的IDisposable丢失,因此无法终止订阅。在此方法返回的IDisposable上调用Dispose会杀死计时器,但不会杀死底层原始事件馈送,该馈送现在不必要地填充队列,没有人从队列中提取事件

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

最后但并非最不重要的一点是,现在我有了定期唤醒的代码,不管是否真的有任何工作要做,在这个奇妙的新反应世界中,我宁愿避免这样做


2.过于复杂的方法 为了解决我最初简单化方法中遇到的问题,我编写了一个更复杂的函数,其行为类似于
IObservable.Delay()
(我使用.NET Reflector读取该代码,并将其用作我函数的基础)。不幸的是,很多样板逻辑,比如
AnonymousObservable
,在系统外部是无法公开访问的。被动代码,所以我不得不复制和粘贴很多代码。这个解决方案看起来是可行的,但考虑到它的复杂性,我不太相信它没有bug


我简直不敢相信,并没有一种方法可以通过使用一些标准的反应式扩展来实现这一点。我讨厌感觉自己在不必要地重新设计轮子,我试图构建的模式似乎是一个相当标准的模式。

这实际上是一个复制品,但我会在这里包括一个摘要(原始版本看起来很混乱,因为它考虑了一些替代方案)


您没有指定语言。如果你把一个放在标签上,熟悉它的人就会看到它。但我的第一个想法是在接收到事件时在堆栈上添加事件(无缓冲),然后打开计时器。@Robert,与其说是堆栈,不如说是队列,不是吗?@spender-当然。显然,我需要去睡觉。是的,这正是我所想的,然而我一直在逃避的是如何使用Rx框架中提供的标准IObservable方法来完成它。我在问题中添加了更多细节,概述了我最初的简单化(和有缺陷的)方法。真正的困难在于如何将原始事件推送到队列中的逻辑以及在常规计时器上从队列中提取的逻辑封装到一个可观察的IObservable中。啊哈!我知道必须有一种相对直接的方式,通过现有运营商的组合来做到这一点。
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());
}
public static IObservable<T> Buffered<T>(this IObservable<T> source, TimeSpan minDelay)
{
    return source.Drain(x => 
        Observable.Empty<int>()
            .Delay(minDelay)
            .StartWith(x)
    );
}
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()))
                );
        });
    }
}