答案 0 :(得分:6)
SO上存在一些类似的问题但不完全像这样。 这是一种可以解决问题的扩展方法。
public static IObservable<IList<TSource>> BufferWithThrottle<TSource>
(this IObservable<TSource> source,
int maxAmount, TimeSpan threshold)
{
return Observable.Create<IList<TSource>>((obs) =>
{
return source.GroupByUntil(_ => true,
g => g.Throttle(threshold).Select(_ => Unit.Default)
.Merge( g.Buffer(maxAmount).Select(_ => Unit.Default)))
.SelectMany(i => i.ToList())
.Subscribe(obs);
});
}
答案 1 :(得分:6)
有趣的运营商。 Supertopi的答案很好,但是可以做出改进。如果maxAmount
很大,和/或通知率很高,那么使用Buffer
将通过分配不久之后被丢弃的缓冲区来刻录GC。
为了在达到GroupBy
后关闭每个maxAmount
Observable,您不需要捕获所有这些元素中的Buffer
只是为了知道它何时已满。根据Supertopi的回答,您可以将其略微改为以下内容。它不是收集Buffer
个maxAmount
元素,而是在流上看到maxAmount
个元素之后发出信号。
public static IObservable<IList<TSource>> BufferWithThrottle<TSource>(this IObservable<TSource> source, int maxAmount, TimeSpan threshold)
{
return Observable.Create<IList<TSource>>((obs) =>
{
return source.GroupByUntil(_ => true,
g => g.Throttle(threshold).Select(_ => Unit.Default)
.Merge(g.Take(maxAmount)
.LastAsync()
.Select(_ => Unit.Default)))
.SelectMany(i => i.ToList())
.Subscribe(obs);
});
}
答案 2 :(得分:2)
很好的解决方案。在我看来,使用exisitng运算符创建行为只是为了方便但不是为了性能。
另外,我们应该总是返回IEnumerable而不是IList。 返回最少派生类型(IEnumerable)将为您提供最大的余地,以便在轨道上更改底层实现。
这是我实现自定义运算符的版本。
public static IObservable<IEnumerable<TValue>> BufferWithThrottle<TValue>(this IObservable<TValue> @this, int maxAmount, TimeSpan threshold)
{
var buffer = new List<TValue>();
return Observable.Create<IEnumerable<TValue>>(observer =>
{
var aTimer = new Timer();
void Clear()
{
aTimer.Stop();
buffer.Clear();
}
void OnNext()
{
observer.OnNext(buffer);
Clear();
}
aTimer.Interval = threshold.TotalMilliseconds;
aTimer.Enabled = true;
aTimer.Elapsed += (sender, args) => OnNext();
var subscription = @this.Subscribe(value =>
{
buffer.Add(value);
if (buffer.Count >= maxAmount)
OnNext();
else
{
aTimer.Stop();
aTimer.Start();
}
});
return Disposable.Create(() =>
{
Clear();
subscription.Dispose();
});
});
}
通过测试性能与其他解决方案相比,它可以节省高达30%的CPU功率并解决内存问题。