C# 在Rx.NET中处理背压,无OnBackpressureTest
我需要在Rx.NET中实现以下算法:C# 在Rx.NET中处理背压,无OnBackpressureTest,c#,f#,system.reactive,reactive-programming,C#,F#,System.reactive,Reactive Programming,我需要在Rx.NET中实现以下算法: 从流中获取最新项目,如果没有新项目,则等待新项目而不阻塞。只有最新的项目重要,其他项目可以删除 将项目输入到SlowFunction并打印输出 重复步骤1 简单的解决方案是: let PrintLatestData (stream: IObservable<_>) = stream.Select(SlowFunction).Subscribe(printfn "%A") 让我们打印最新数据(流:IObservable)= stream.
流
中获取最新项目,如果没有新项目,则等待新项目而不阻塞。只有最新的项目重要,其他项目可以删除SlowFunction
并打印输出let PrintLatestData (stream: IObservable<_>) =
stream.Select(SlowFunction).Subscribe(printfn "%A")
让我们打印最新数据(流:IObservable)=
stream.Select(SlowFunction.Subscribe)(printfn“%A”)
但是,此解决方案不起作用,因为平均而言,stream
发出项目的速度比SlowFunction
消耗项目的速度快。由于Select
不会删除项目,而是尝试按从最旧到最新的顺序处理每个项目,因此随着程序的运行,发送和打印项目之间的延迟将逐渐增大。只有最新的项目应采取从流,以避免这种无限增长的背压
我搜索了文档,在RxJava中找到了一个名为
onBackpressureLatest
的方法,据我所知,该方法可以实现我上面描述的功能。但是,Rx.NET中不存在该方法。如何在Rx.NET中实现这一点?同步/异步建议可能会有所帮助,但是,鉴于慢速函数总是比事件流慢,使其异步可能允许您并行处理(在线程池上进行观察),代价是(最终)线程耗尽或通过上下文切换增加更多延迟。对我来说,这听起来不是一个解决办法
我建议您看看Dave Sexton编写的开源Rxx“内省”操作符。这些可能会改变您从中获取最新信息的缓冲/节流周期,因为队列由于消耗速度慢而备份。如果慢功能突然变快,它根本不会缓冲任何东西。如果速度变慢,它将缓冲更多。
您必须检查是否有“最新发件人”类型,或者仅修改现有类型以满足您的需要。例如,使用缓冲区,只取缓冲区中的最后一项,或进一步增强,只在内部存储最新项。谷歌“Rxx”,你可以在Github的某个地方找到它
如果“慢功能”的时间是相当可预测的,那么一个更简单的方法就是简单地将流的节流量超过这个时间。很明显,我不是指标准的rx“油门”,而是指一个可以通过更新而不是旧的。这里有很多解决这类问题的方法 我想您应该使用类似于
ObserveLatestOn
的东西。它用一个值和一个标志有效地替换了传入事件的队列
詹姆斯·沃德在这里写了一篇博客
这个概念在GUI应用程序中被大量使用,这些应用程序无法相信服务器推送数据的速度
您还可以在reactivetrader中看到一个实现
以及解释ReactiveTrader的支持演示文稿
需要说明的是,这是一种减载算法,而不是背压算法。不久前,我也遇到了同样的问题,我没有找到一个内置的操作程序能够做到这一点。所以我写了我自己的,我称之为
Latest
。实现起来并不简单,但我发现它在我当前的项目中非常有用
它的工作原理是这样的:当观察者忙于处理之前的通知时(当然是在它自己的线程上),它会将最后的n个通知(n>=0)排队,并在观察者空闲时将其下一个s。因此:
最新(0)
:仅在观察者空闲时观察到达的项目
最新版本(1)
:始终遵守最新版本
Latest(1000)
(例如):通常处理所有项目,但如果某个项目被卡在了行中,与其得到一个OutOfMemoryException,不如错过一些项目
Latest(int.MaxValue)
:决不要错过任何项目,但要在生产者和消费者之间保持负载平衡
因此,您的代码将是:stream.Latest(1)、Select(SlowFunction)、Subscribe(printfn“%A”)
签名如下所示:
/// <summary>
/// Avoids backpressure by enqueuing items when the <paramref name="source"/> produces them more rapidly than the observer can process.
/// </summary>
/// <param name="source">The source sequence.</param>
/// <param name="maxQueueSize">Maximum queue size. If the queue gets full, less recent items are discarded from the queue.</param>
/// <param name="scheduler">Optional, default: <see cref="Scheduler.Default"/>: <see cref="IScheduler"/> on which to observe notifications.</param>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="maxQueueSize"/> is negative.</exception>
/// <remarks>
/// A <paramref name="maxQueueSize"/> of 0 observes items only if the subscriber is ready.
/// A <paramref name="maxQueueSize"/> of 1 guarantees to observe the last item in the sequence, if any.
/// To observe the whole source sequence, specify <see cref="int.MaxValue"/>.
/// </remarks>
public static IObservable<TSource> Latest<TSource>(this IObservable<TSource> source, int maxQueueSize, IScheduler scheduler = null)
//
///当观察者生成项目的速度快于观察者处理项目的速度时,通过将项目排队来避免背压。
///
///源序列。
///最大队列大小。如果队列已满,则较新的项目将从队列中丢弃。
///可选,默认::要在其上观察通知。
///是空的。
///答案是否定的。
///
///只有在订阅者准备就绪时,0才能观察项目。
///如果有,1保证观察序列中的最后一项。
///要观察整个源序列,请指定。
///
公共静态IObservable Latest(此IObservable源,int maxQueueSize,IsScheduler scheduler=null)
实现太大,无法在这里发布,但如果有人感兴趣,我很乐意与大家分享。让我知道。您可以以您知道的间隔对流进行采样慢功能可以处理。下面是一个java示例:
TestScheduler ts=newtestscheduler();
可观测流=可观测的间隔(1,TimeUnit.ms,ts).take(500);
stream.sample(100,TimeUnit.ms,ts).subscribe(System.out::println);
ts.advanceTimeBy(1000,时间单位毫秒);
98
198
298
398
498
499
示例
不会产生背压,并且始终获取流中的最新值,因此它满足您的要求。另外,sample
不会两次发送相同的值(从上面可以看出,499
只打印一次)
我认为这将是一个有效的C
/F
解决方案:
static IDisposable PrintLatestData<T>(IObservable<T> stream) {
return stream.Sample(TimeSpan.FromMilliseconds(100))
.Select(SlowFunction)
.Subscribe(Console.WriteLine);
}
静态IDisposable PrintLatestData(IObservable流){
返回stream.Sample(TimeSpan.From毫秒(100))
.选择(慢功能)
.Subscribe(Console.WriteLine);
}
让我们打印最新数据(流:IObservable)=
str
let PrintLatestData (stream: IObservable<_>) =
stream.Sample(TimeSpan.FromMilliseconds(100))
.Select(SlowFunction)
.Subscribe(printfn "%A")