Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.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# 以偶数间隔推送缓冲事件的一种方法_C#_.net_System.reactive - Fatal编程技术网

C# 以偶数间隔推送缓冲事件的一种方法

C# 以偶数间隔推送缓冲事件的一种方法,c#,.net,system.reactive,C#,.net,System.reactive,我试图实现的是缓冲来自一些IObservable的传入事件(它们以突发的方式出现),并进一步释放它们,但一个接一个地释放,间隔甚至相等。 像这样: -oo-ooo-oo------------------oooo-oo-o--------------> -o--o--o--o--o--o--o--------o--o--o--o--o--o--o----> 由于我对Rx非常陌生,所以我不确定是否已经有一个主题或一个操作员这样做了。也许可以通过构图来完成 更新: 多亏了 为了指出D

我试图实现的是缓冲来自一些IObservable的传入事件(它们以突发的方式出现),并进一步释放它们,但一个接一个地释放,间隔甚至相等。 像这样:

-oo-ooo-oo------------------oooo-oo-o-------------->

-o--o--o--o--o--o--o--------o--o--o--o--o--o--o---->
由于我对Rx非常陌生,所以我不确定是否已经有一个主题或一个操作员这样做了。也许可以通过构图来完成

更新:

多亏了 为了指出Drain操作符,我找到了另一个Drain操作符用法。以下是我如何让它在WPF应用程序中工作的:

    .Drain(x => {
        Process(x);
        return Observable.Return(new Unit())
            .Delay(TimeSpan.FromSeconds(1), Scheduler.Dispatcher );
    }).Subscribe();
我玩得很开心,因为省略scheduler参数会导致应用程序在调试模式下崩溃,而不会出现任何异常(我需要学习如何在Rx中处理异常)。 Process方法直接修改UI状态,但我想用它来创建IObservable(使用ISubject?)相当简单

更新:

与此同时,我一直在使用ISubject进行实验,下面的类实现了我想要的功能—它可以及时释放缓冲的Ts:

public class StepSubject<T> : ISubject<T>
{
    IObserver<T> subscriber;
    Queue<T> queue = new Queue<T>();
    MutableDisposable cancel = new MutableDisposable();
    TimeSpan interval;
    IScheduler scheduler;
    bool idle = true;

    public StepSubject(TimeSpan interval, IScheduler scheduler)
    {
        this.interval = interval;
        this.scheduler = scheduler;
    }

    void Step()
    {
        T next;
        lock (queue)
        {
            idle = queue.Count == 0;
            if (!idle)
                next = queue.Dequeue();
        }

        if (!idle)
        {
            cancel.Disposable = scheduler.Schedule(Step, interval);
            subscriber.OnNext(next);
        }
    }

    public void OnNext(T value)
    {
        lock (queue)
            queue.Enqueue(value);

        if (idle)
            cancel.Disposable = scheduler.Schedule(Step);
    }

    public IDisposable Subscribe(IObserver<T> observer)
    {
        subscriber = observer;
        return cancel;
    }
}
公共类步骤主题:ISubject
{
IObserver用户;
队列=新队列();
MutableDisposable cancel=新的MutableDisposable();
时间间隔;
IScheduler调度器;
bool idle=true;
公共StepSubject(时间跨度间隔,ISScheduler调度程序)
{
这个。间隔=间隔;
this.scheduler=调度程序;
}
无效步骤()
{
T下一步;
锁(队列)
{
空闲=队列。计数==0;
如果(!空闲)
next=queue.Dequeue();
}
如果(!空闲)
{
cancel.Disposable=scheduler.Schedule(步骤,间隔);
subscriber.OnNext(下一个);
}
}
公共void OnNext(T值)
{
锁(队列)
queue.Enqueue(值);
如果(空闲)
cancel.Disposable=scheduler.Schedule(步骤);
}
公共IDisposable订阅(IObserver观察员)
{
订户=观察员;
返回取消;
}
}

为了清晰起见,这个幼稚的实现从OnCompleted和OnError中剥离出来,并且只允许一次订阅。

它实际上比听起来更狡猾

使用
Delay
不起作用,因为这些值仍然会大量出现,只是稍微延迟

Interval
combinelateest
Zip
一起使用不起作用,因为前者将导致跳过源值,后者将缓冲区间值

我认为新的
Drain
操作符(),加上
Delay
应该可以做到:

source.Drain(x => Observable.Empty<int>().Delay(TimeSpan.FromSeconds(1)).StartWith(x));

为了完整起见,这里是Richard建议的Drain()方法的一个替代(更紧凑)版本:

public static IObservable<T2> SelectManySequential<T1, T2>(
    this IObservable<T1> source, 
    Func<T1, IObservable<T2>> selector
)
{
    return source
        .Select(x => Observable.Defer<T2>(() => selector(x)))
        .Concat();
}
publicstaticiobservable SelectManySequential(
这是一个可观测的来源,
函数选择器
)
{
返回源
.Select(x=>Observable.Defer(()=>selector(x)))
.Concat();
}
查看Rx论坛中的线程

更新: 我意识到我使用的Concat()重载是我个人的Rx扩展之一,它(尚未)是框架的一部分。我为这个错误感到抱歉。。当然,这使得我的解决方案没有我想象的那么优雅

然而,为了完整性,我在这里发布了我的Conact()扩展方法重载:

public static IObservable<T> Concat<T>(this IObservable<IObservable<T>> source)
{
    return Observable.CreateWithDisposable<T>(o =>
    {
        var lockCookie = new Object();
        bool completed = false;
        bool subscribed = false;
        var waiting = new Queue<IObservable<T>>();
        var pendingSubscription = new MutableDisposable();

        Action<Exception> errorHandler = e =>
        {
            o.OnError(e);
            pendingSubscription.Dispose();
        };

        Func<IObservable<T>, IDisposable> subscribe = null;
        subscribe = (ob) =>
        {
            subscribed = true;
            return ob.Subscribe(
                o.OnNext,
                errorHandler,
                () =>
                {
                    lock (lockCookie)
                    {
                        if (waiting.Count > 0)
                            pendingSubscription.Disposable = subscribe(waiting.Dequeue());
                        else if (completed)
                            o.OnCompleted();
                        else
                            subscribed = false;
                    }
                }
            );
        };

        return new CompositeDisposable(pendingSubscription,
            source.Subscribe(
                n =>
                {
                    lock (lockCookie)
                    {
                        if (!subscribed)
                            pendingSubscription.Disposable = subscribe(n);
                        else
                            waiting.Enqueue(n);
                    }

                },
                errorHandler
                , () =>
                {
                    lock (lockCookie)
                    {
                        completed = true;
                        if (!subscribed)
                            o.OnCompleted();
                    }
                }
            )
        );
    });
}
公共静态IObservable Concat(此IObservable源)
{
返回可观察的。CreateWithDisposable(o=>
{
var lockCookie=新对象();
bool completed=false;
bool=false;
var waiting=新队列();
var pendingSubscription=new MutableDisposable();
动作errorHandler=e=>
{
o、 OnError(e);
pendingSubscription.Dispose();
};
Func subscribe=null;
订阅=(ob)=>
{
订阅=真;
返回ob.Subscribe(
o、 OnNext,
错误处理程序,
() =>
{
锁(lockCookie)
{
如果(waiting.Count>0)
pendingSubscription.Disposable=subscribe(waiting.Dequeue());
否则,如果(已完成)
o、 未完成();
其他的
订阅=假;
}
}
);
};
返回新的CompositeDisposable(pendingSubscription,
来源:订阅(
n=>
{
锁(lockCookie)
{
如果(!已订阅)
pendingSubscription.Disposable=订阅(n);
其他的
排队(n);
}
},
错误处理程序
, () =>
{
锁(lockCookie)
{
完成=正确;
如果(!已订阅)
o、 未完成();
}
}
)
);
});
}
现在用我自己的武器打败我自己: 同样的Concat()方法可以用Richard Szalay的绝妙方式编写得更加优雅:

public static IObservable<T> Concat<T>(this IObservable<IObservable<T>> source)
{
    return Observable.Defer(() =>
    {
        BehaviorSubject<Unit> queue = new BehaviorSubject<Unit>(new Unit());
        return source
            .Zip(queue, (v, q) => v)
            .SelectMany(v => 
                v.Do(_ => { }, () => queue.OnNext(new Unit()))
            );
    });
}
公共静态IObservable Concat(此IObservable源)
{
返回可观察的延迟(()=>
{
BehaviorSubject队列=新的BehaviorSubject(新单元());
返回源
.Zip(队列,(v,q)=>v)
.SelectMany(v=>
v、 Do(=>{},()=>queue.OnNext(新单元())
);
});
}

因此,信用属于Richard.:-)

我就是这样做的,只是使用一个显式队列(ReactiveCollection只是WPF的ObservableCollection-ReactiveCollection.ItemsAdded OnNext for eac的一个奇特版本)
public static IObservable<T> Concat<T>(this IObservable<IObservable<T>> source)
{
    return Observable.Defer(() =>
    {
        BehaviorSubject<Unit> queue = new BehaviorSubject<Unit>(new Unit());
        return source
            .Zip(queue, (v, q) => v)
            .SelectMany(v => 
                v.Do(_ => { }, () => queue.OnNext(new Unit()))
            );
    });
}
public static ReactiveCollection<T> CreateCollection<T>(this IObservable<T> FromObservable, TimeSpan? WithDelay = null)
{
    var ret = new ReactiveCollection<T>();
    if (WithDelay == null) {
        FromObservable.ObserveOn(RxApp.DeferredScheduler).Subscribe(ret.Add);
        return ret;
    }

    // On a timer, dequeue items from queue if they are available
    var queue = new Queue<T>();
    var disconnect = Observable.Timer(WithDelay.Value, WithDelay.Value)
        .ObserveOn(RxApp.DeferredScheduler).Subscribe(_ => {
            if (queue.Count > 0) { 
                ret.Add(queue.Dequeue());
            }
        });

    // When new items come in from the observable, stuff them in the queue.
    // Using the DeferredScheduler guarantees we'll always access the queue
    // from the same thread.
    FromObservable.ObserveOn(RxApp.DeferredScheduler).Subscribe(queue.Enqueue);

    // This is a bit clever - keep a running count of the items actually 
    // added and compare them to the final count of items provided by the
    // Observable. Combine the two values, and when they're equal, 
    // disconnect the timer
    ret.ItemsAdded.Scan0(0, ((acc, _) => acc+1)).Zip(FromObservable.Aggregate(0, (acc,_) => acc+1), 
        (l,r) => (l == r)).Where(x => x != false).Subscribe(_ => disconnect.Dispose());

    return ret;
}