C# Rx顺序分组(分区流)
我有一系列事件:C# Rx顺序分组(分区流),c#,reactive-programming,system.reactive,C#,Reactive Programming,System.reactive,我有一系列事件: event.EventTime: 1s-----2s----3s----4s----5s----6s--- stream: A-B-C--D-----------------E-F---G-H-- 事件如下所示: public class Event { public DateTime EventTime { get; set; } public int Value { get; set; } } event.EventTime: 1s-----2
event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream: A-B-C--D-----------------E-F---G-H--
事件如下所示:
public class Event
{
public DateTime EventTime { get; set; }
public int Value { get; set; }
}
event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream: A---C--D-------------------F-----H--
EventTime
应与事件到达的时间相对应,但可能会有一点延迟。不过,这些活动不应该无序进行
现在,当我指定一个分组间隔(比如1秒)时,我希望流按如下方式分组
1s-------2s----3s----4s----5s-----6s---
[A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H]
(注意空间隔)
我尝试过使用缓冲区
,但遗憾的是,我需要按EventTime进行分区,而不是按System.DateTime.Now进行分区。即使使用边界,我也需要某种前瞻性,因为当我使用缓冲区(2,1)作为边界并比较[0]和[1]时,即使[1]成功地破坏了缓冲区,它仍然会被插入到旧的缓冲区而不是新的缓冲区中。我还尝试了GroupBy
,但只有在输入流完成后才能生成组。这是不应该发生的。然后我试了一些这个东西:
var intervalStart = GetIntervalStartLocal(DateTime.Now) + intervalLength;
var intervals = Observable.Timer(intervalStart, intervalLength);
var eventsAsObservables = intervals.GroupJoin<long, Event, long, Event, (DateTime, IObservable<Event>)>(
data,
_ => Observable.Never<long>(),
_ => Observable.Never<Event>(),
(intervalNumber, events) => {
var currentIntervalStart = intervalStart + intervalNumber*intervalLength;
var eventsInInterval = events
.SkipWhile(e => GetIntervalStartLocal(e.EventTime) < currentIntervalStart)
.TakeWhile(e => GetIntervalStartLocal(e.EventTime) == currentIntervalStart);
return (currentIntervalStart, eventsInInterval);
});
var eventsForIntervalsAsObservables = eventsAsObservables.SelectMany(g => {
var lists = g.Item2.Aggregate(new List<Event>(), (es, e) => { es.Add(e); return es; });
return lists.Select(l => (intervalStart: g.Item1, events: l));
});
var task = eventsForIntervalsAsObservables.ForEachAsync(es => System.Console.WriteLine(
$"=[{es.intervalStart.TimeOfDay}]= " + string.Join("; ", es.events.Select(e => e.EventTime.TimeOfDay))));
await task;
然后构造(正确地说,根据它的操作):
我想我一定是做错了什么,因为基于流事件值对流进行分区应该不会那么困难。您需要澄清您想要什么。鉴于此:
time : 1s-------2s----3s----4s----5s-----6s---
stream: A-B-C----D-----------------E-F----G-H-- (actual)
group : [A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H] (desired result)
不清楚这里的“时间”是您的活动时间戳还是实际时间。如果是实际时间,那么这当然是不可能的:在C到达之前,你不能通过ABC列表。如果您指的是您的事件时间戳,那么缓冲区
或者窗口
将必须知道何时停止,这并不是那么容易做到的
GroupBy
对我的作用如下:
var sampleSource = Observable.Interval(TimeSpan.FromMilliseconds(400))
.Timestamp()
.Select(t => new Event { EventTime = t.Timestamp.DateTime, Value = (int)t.Value });
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.Dump(); //LinqPad
唯一的问题是,每个组都没有一个严格的标准,所以这是一个巨大的内存泄漏。因此,您可以添加计时器来关闭组:
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.Select(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2)))) //group closes 2 seconds after opening
.Dump(); //LinqPad
此结束操作还允许我们返回带有.ToList()
的列表,而不是可观察的列表:
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.SelectMany(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2))).ToList())
.Dump(); //LinqPad
为什么.Window(…)
不适合您?
sampleSource
.GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
.SelectMany(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2))).ToList())
.Dump(); //LinqPad