我有一个以快速(亚毫秒)到达的数值流,我想在屏幕上显示它们的“即时值”,出于可用性原因,我应该对该流进行缩减采样,使用更新最后一个值可配置时间间隔。该配置将通过拖动滑块按用户首选项完成。
所以我想做的是将源流的最后一个值存储在一个变量中,并有一个自动重新触发计时器,用该变量的值更新显示的值。
我想使用RX,就像这样:
Observable.FromEventPattern<double>(_source, "NewValue")
.Sample(TimeSpan.FromMilliseconds(100))
.Subscribe(ep => instantVariable = ep.EventArgs)
问题是,就我所知,我不能动态更改间隔。
我可以想象有很多方法可以使用计时器,但我更喜欢使用RX。
答案 0 :(得分:3)
假设您可以将样本大小更改建模为可观察的,您可以这样做:
IObservable<int> sampleSizeObservable;
var result = sampleSizeObservable
.Select(i => Observable.FromEventPattern<double>(_source, "NewValue")
.Sample(TimeSpan.FromMilliseconds(i))
)
.Switch();
Switch
基本上可以做你想要的,但是通过Rx。它不会“改变”间隔:Observable(通常)应该是不可变的。相反,只要样本大小发生变化,它就会创建一个新的可观察样本,订阅新的observable,将订阅丢弃到旧的,并将这两个订阅合并在一起,使其看起来与客户订阅者无缝。
答案 1 :(得分:1)
这是一个自定义Sample
运算符,其时间间隔可以在订阅生命周期之前或期间随时更改。
/// <summary>Samples the source observable sequence at a dynamic interval
/// controlled by a delegate.</summary>
public static IObservable<T> Sample<T>(this IObservable<T> source,
out Action<TimeSpan> setInterval)
{
var intervalController = new ReplaySubject<TimeSpan>(1);
setInterval = interval => intervalController.OnNext(interval);
return source.Publish(shared => intervalController
.Select(timeSpan => timeSpan == Timeout.InfiniteTimeSpan ?
Observable.Empty<long>() : Observable.Interval(timeSpan))
.Switch()
.WithLatestFrom(shared, (_, x) => x)
.TakeUntil(shared.LastOrDefaultAsync()));
}
out Action<TimeSpan> setInterval
参数是控制采样间隔的机制。可以使用任何非负TimeSpan
参数或具有暂停采样作用的特殊值Timeout.InfiniteTimeSpan
来调用它。
在源序列产生的值比期望的采样间隔慢的情况下,此运算符与内置的Sample
不同。内置的Sample
将采样调整为信号源的速度,而不会重复发射相同的值。相反,此运算符会保持自己的速度,从而可以多次发出相同的值。如果不希望出现这种情况,可以在DistinctUntilChanged
之后附加Sample
运算符。
用法示例:
var subscription = Observable.FromEventPattern<double>(_source, "NewValue")
.Sample(out var setSampleInterval)
.Subscribe(ep => instantVariable = ep.EventArgs);
setSampleInterval(TimeSpan.FromMilliseconds(100)); // Initial sampling interval
//...
setSampleInterval(TimeSpan.FromMilliseconds(500)); // Slow down
//...
setSampleInterval(Timeout.InfiniteTimeSpan); // Suspend
答案 2 :(得分:0)
如果数据进入&#34;亚毫秒&#34; intervalls,你需要处理的是realtime programming。使用.NET Framework的垃圾收集 - 与使用C#的大多数其他东西 - 相距甚远。你可以在某些地区靠近。但是你永远无法保证程序能够跟上数据的摄取量。
除此之外,你想要的听起来像速率限制代码。 Interval不会经常运行的代码。我为多线程示例编写了此示例代码,但它应该让您开始使用Idea:
integer interval = 20;
DateTime dueTime = DateTime.Now.AddMillisconds(interval);
while(true){
if(DateTime.Now >= dueTime){
//insert code here
//Update next dueTime
dueTime = DateTime.Now.AddMillisconds(interval);
}
else{
//Just yield to not tax out the CPU
Thread.Sleep(1);
}
}
请注意,DateTime实际上具有有限的精度,通常不会低于5-20毫秒。秒表更精确。但老实说,每秒超过60次更新(17 ms Intervall)的任何内容都不会是人类可读的。
另一个问题实际上是编写GUI非常昂贵。如果每个用户触发的事件只写一次,您将永远不会注意到。但是,如果您从循环中发送更新(包括在另一个线程中运行的更新),您可以快速解决问题。在我第一次使用多线程测试的时候,我实际上做了这么多复杂的进度报告,我最终简单地重载了用线程来改变GUI线程。
答案 3 :(得分:0)
尝试这个,让我知道它是否有效:
Observable
.FromEventPattern<double>(_source, "NewValue")
.Window(() => Observable.Timer(TimeSpan.FromMilliseconds(100)))
.SelectMany(x => x.LastAsync());