Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.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#_System.reactive_Reactive Programming - Fatal编程技术网

C# 反应式编程使时间序列值正常化

C# 反应式编程使时间序列值正常化,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,我有一个远程程序,通过套接字连接每隔10毫秒发送一次更新的测量值。在我的客户机程序中,我将这个套接字包装在一个可观察的容器中,该容器生成了这些测量值。对于我的用例来说,测量值以10毫秒的间隔到达是很重要的。当然,这种情况不会发生,因为网络延迟会使每条消息的到达时间稍早或稍晚 所以基本上我在我的远程pc上有一个程序,它通过套接字连接发送数据 --为10毫秒 o--o--o--o--o--o--o--o--o--o--... 由于网络延迟,在我的客户机上会变成这样 o-o---o-o--o---o

我有一个远程程序,通过套接字连接每隔10毫秒发送一次更新的测量值。在我的客户机程序中,我将这个套接字包装在一个可观察的容器中,该容器生成了这些测量值。对于我的用例来说,测量值以10毫秒的间隔到达是很重要的。当然,这种情况不会发生,因为网络延迟会使每条消息的到达时间稍早或稍晚

所以基本上我在我的远程pc上有一个程序,它通过套接字连接发送数据

--
为10毫秒

o--o--o--o--o--o--o--o--o--o--...
由于网络延迟,在我的客户机上会变成这样

o-o---o-o--o---o--o-o--o-o-...
现在在我的observable中,我想“归一化”它,这样它会再次每10毫秒发出一个值

--o--o--o--o--o--o--o--o--o--o...
当然,这意味着我必须引入一个缓冲时间,它将存储值并以10毫秒的间隔发出它们。有什么办法可以做到这一点吗

下面是一些测试代码,它们将按照我上面描述的方式发出事件

using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Microsoft.Reactive.Testing;

public class Program
{
    protected static event EventHandler<EventArgs> CancelEvent;

    private static Random random = new Random();

    private static double GetRandomNumber(double minimum, double maximum)
    { 
        return random.NextDouble() * (maximum - minimum) + minimum;
    }

    public static void Main()
    {
        var completed = false;

        var scheduler = new TestScheduler();

        var observable = Observable
            .Interval(TimeSpan.FromMilliseconds(7.0), scheduler)
            .SelectMany(e => Observable
                .Return(e, scheduler)
                .Delay(TimeSpan.FromMilliseconds(GetRandomNumber(0.0, 6.0)), scheduler)
            )
            .TimeInterval(scheduler)
            .Select(t => t.Interval.Milliseconds);

        var fromEvent = Observable.FromEventPattern<EventArgs>(
            p => CancelEvent += p,
            p => CancelEvent -= p,
            scheduler
        );

        var cancellable = observable.TakeUntil(fromEvent);

        var results = new List<int>();

        using (cancellable.Subscribe(
            results.Add,
            e => { throw new Exception("No exception is planned! {0}", e); },
            () => { completed = true; })
        )
        {
            scheduler.AdvanceBy(TimeSpan.FromSeconds(3.5).Ticks);
            CancelEvent(null, new EventArgs());
            scheduler.AdvanceBy(TimeSpan.FromSeconds(3).Ticks);
        }

        Console.WriteLine("Have I completed indeed? {0}", completed);
        Console.WriteLine("What emit time deltas been registered before cancellation?\n\t{0}", string.Join("ms\n\t", results));
    }
}
使用系统;
使用System.Collections.Generic;
使用系统反应性一次性用品;
使用System.Reactive.Linq;
使用System.Threading.Tasks;
使用Microsoft.Reactive.Testing;
公共课程
{
受保护的静态事件EventHandler CancelEvent;
私有静态随机=新随机();
私有静态双GetRandomNumber(最小值加倍,最大值加倍)
{ 
返回random.NextDouble()*(最大值-最小值)+最小值;
}
公共静态void Main()
{
var completed=假;
var scheduler=newtestscheduler();
var可观测=可观测
.Interval(时间跨度从毫秒(7.0),调度程序)
.选择多个(e=>可观察
.返回(e,调度程序)
.Delay(TimeSpan.From毫秒(GetRandomNumber(0.0,6.0)),调度程序)
)
.时间间隔(调度程序)
.Select(t=>t.Interval.millities);
var fromEvent=Observable.FromEventPattern(
p=>CancelEvent+=p,
p=>CancelEvent-=p,
调度程序
);
var Cancelable=可观测的。TakeUntil(fromEvent);
var results=新列表();
使用(cancelable.Subscribe)(
结果。添加,
e=>{抛出新异常(“没有计划异常!{0}”,e);},
()=>{completed=true;})
)
{
调度程序.AdvanceBy(TimeSpan.FromSeconds(3.5).Ticks);
CancelEvent(null,新的EventArgs());
调度程序.AdvanceBy(TimeSpan.FromSeconds(3).Ticks);
}
WriteLine(“我确实完成了吗?{0}”,已完成);
Console.WriteLine(“取消之前注册了什么发射时间增量?\n\t{0}”,string.Join(“ms\n\t”,results));
}
}

这在理论上类似于

该解决方案如下所示:

var source = new Subject<double>();
var bufferTime = TimeSpan.FromMilliseconds(100);
var normalizedSource = source
    .Delay(bufferTime)
    .Drain(x => Observable.Empty<int>().Delay(TimeSpan.FromMilliseconds(10)));

然而,我认为你会遇到10毫秒限定符的问题。这时间安排得太少了。如果我没记错的话,任何小于15毫秒的延迟都会被调度程序忽略,并立即触发。有鉴于此,即使您使用了更大的间隔(我尝试了100毫秒),由于操作系统上下文切换等原因,您也会得到一些差异。

您可以简单地
.Zip
使用
.interval(TimeSpan.frommilluses(10.0))
?另外,请记住,Windows不是一个实时操作系统,无论您做什么,都无法将计时降至10毫秒。只有在时间间隔始终低于源代码时,Zip才会工作。如果有一个很长的延迟,然后是一个突发,那么它们会同时聚集在一起。
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()))
                );
        });
    }
}