我有一个远程程序,它通过套接字连接每10毫秒发送一次更新的测量。在我的客户端程序中,我将此套接字包装在一个生成这些测量的observable中。对于我的用例,测量以10毫秒的间隔到达是很重要的。当然,这种情况并没有发生,因为网络延迟使得它在每个消息之前或之后都会到达。
所以我在远程PC上的基本功能就是在套接字连接上发送它的程序。
--
是10毫秒
o--o--o--o--o--o--o--o--o--o--...
由于网络延迟,我的客户端会出现这种情况。
o-o---o-o--o---o--o-o--o-o-...
现在,在我的观察中,我希望"正常化"这样它每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));
}
}
答案 0 :(得分:2)
这在理论上类似于A way to push buffered events in even intervals。
该解决方案如下所示:
var source = new Subject<double>();
var bufferTime = TimeSpan.FromMilliseconds(100);
var normalizedSource = source
.Delay(bufferTime)
.Drain(x => Observable.Empty<int>().Delay(TimeSpan.FromMilliseconds(10)));
... Drain
的定义如下:
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()))
);
});
}
}
但是,我认为你将遇到10毫秒限定符的问题。安排的时间太短了。如果我没记错的话,调度程序会忽略任何小于15毫秒的延迟并立即触发。鉴于此,即使您使用了更大的间隔(我尝试了100毫秒),由于操作系统上下文切换等原因,您将获得一些差异。