C# 反应式扩展:成批处理事件+;在每个批次之间添加延迟
我有一个应用程序,它在某些时候几乎同时引发1000个事件。我想做的是将事件批处理成50个项目的块,并开始每10秒处理一次。在开始新的批处理之前,无需等待批处理完成 例如:C# 反应式扩展:成批处理事件+;在每个批次之间添加延迟,c#,system.reactive,windows-runtime,C#,System.reactive,Windows Runtime,我有一个应用程序,它在某些时候几乎同时引发1000个事件。我想做的是将事件批处理成50个项目的块,并开始每10秒处理一次。在开始新的批处理之前,无需等待批处理完成 例如: 10:00:00: 10000 new events received 10:00:00: StartProcessing (events.Take(50)) 10:00:10: StartProcessing (events.Skip(50).Take(50)) 10:00:15: StartProcessing (even
10:00:00: 10000 new events received
10:00:00: StartProcessing (events.Take(50))
10:00:10: StartProcessing (events.Skip(50).Take(50))
10:00:15: StartProcessing (events.Skip(100).Take(50))
有什么办法可以做到这一点吗?我认为反应式扩展是可行的,但其他解决方案也是可以接受的
我试着从这里开始:
var bufferedItems = eventAsObservable
.Buffer(15)
.Delay(TimeSpan.FromSeconds(5)
但是注意到延迟并没有像我希望的那样起作用,而是所有批次同时开始,尽管延迟了5秒
我还测试了Window方法,但没有发现行为上的任何差异。我想窗口中的TimeSpan实际上意味着“在接下来的10秒内处理所有事件:
var bufferedItems = eventAsObservable
.Window(TimeSpan.FromSeconds(10), 5)
.SelectMany(x => x)
.Subscribe(DoProcessing);
我正在使用Rx Main 2.0.20304-beta版。试试这个:
var bufferedItems = eventAsObservable
.Buffer(50)
.Do(_ => { Thread.Sleep(10000); });
如果不希望睡眠线程,可以执行以下操作:
var tick = Observable.Interval(TimeSpan.FromSeconds(5));
eventAsObservable
.Buffer(50)
.Zip(tick, (res, _) => res)
.Subscribe(DoProcessing);
有一个特定的缓冲区方法重载用于此:
这是一个令人惊讶的难以解决的问题。更重要的是,使用
Zip
操作符将可观测值与可观测值对齐的诱人想法。间隔,存在缺陷且效率极低。Zip
操作符的主要问题是,当与不对称可观测值一起使用时,它会缓冲ele最快生成的可观察对象的集合,在长期订阅期间可能导致大量内存分配。IMHO此运算符的使用应限于预期在长期内生成相等(或接近相等)数量元素的可观察对象对
Zip
+Observable.Interval
组合的错误行为在Observable.Interval
发出的值比源可观测值快时出现。在这种情况下,Observable.Interval
发出的多余值会被缓冲,因此当源可观测值发出下一个元素时,就会出现already缓冲的间隔
值以形成一对,从而违反“元素之间的最小间隔”策略
下面是自定义WithInterval
运算符的实现,该运算符在可观测序列的连续元素之间施加最小间隔。然后,该运算符将用于解决此问题的特定问题,该问题涉及缓冲区而不是单个元素:
/// <summary>Intercepts a minimum interval between consecutive elements of an
/// observable sequence.</summary>
public static IObservable<T> WithInterval<T>(this IObservable<T> source,
TimeSpan interval, IScheduler scheduler = null)
{
return source
.Scan((Observable.Return(0L), (IObservable<T>)null), (state, x) =>
{
var (previousTimer, _) = state;
var timer = (scheduler != null ? Observable.Timer(interval, scheduler)
: Observable.Timer(interval)).PublishLast();
var delayed = previousTimer.Select(_ => x).Finally(() => timer.Connect());
return (timer, delayed);
})
.Select(e => e.Item2)
.Concat();
}
用法示例:
var subscription = eventAsObservable
.BatchWithInterval(50, TimeSpan.FromSeconds(10))
.Subscribe(DoProcessing);
我希望我能不止一次地更新它!我花了三个小时试图解决它。我有一个类似的问题。但我希望下一批不在最后一批完成之前开始。@Zuendi您可能会发现这很有趣:这不是OP想要的。他希望每5秒处理一次。如果缓冲区有50秒,这个扩展也会触发元素。这可能在5秒前发生。。。
/// <summary>Projects each element of an observable sequence into consecutive
/// non-overlapping buffers which are produced based on element count information,
/// intercepting a minimum interval between consecutive buffers.</summary>
public static IObservable<IList<T>> BatchWithInterval<T>(this IObservable<T> source,
int count, TimeSpan interval, IScheduler scheduler = null)
{
return source.Buffer(count).WithInterval(interval, scheduler);
}
var subscription = eventAsObservable
.BatchWithInterval(50, TimeSpan.FromSeconds(10))
.Subscribe(DoProcessing);