我有一堆事件进来,我必须毫不犹豫地执行所有这些事件,但我想确保它们在适当的时间段进行缓冲和消耗。有人有解决方案吗?
我无法在Rx中找到任何可以在不丢失事件的情况下执行此操作的操作符(Throttle - looses事件)。我也考虑过Buffered,Delay等......无法找到一个好的解决方案。
我试过把一个计时器放在中间,但不知何故它根本不起作用:
GetInitSequence()
.IntervalThrottle(TimeSpan.FromSeconds(5))
.Subscribe(
item =>
{
Console.WriteLine(DateTime.Now);
// Process item
}
);
public static IObservable<T> IntervalThrottle<T>(this IObservable<T> source, TimeSpan dueTime)
{
return Observable.Create<T>(o =>
{
return source.Subscribe(x =>
{
new Timer(state =>
o.OnNext((T)state), x, dueTime, TimeSpan.FromMilliseconds(-1));
}, o.OnError, o.OnCompleted);
});
}
答案 0 :(得分:12)
问题不是100%明确,所以我做了一些假设。
Observable.Delay
不是您想要的,因为这会在每个事件到来时产生延迟,而不是创建处理的时间间隔。
Observable.Buffer
不是您想要的,因为这会导致每个给定间隔中的所有事件都传递给您,而不是一次传递给您。
所以我相信你正在寻找一种可以创造某种节拍器的解决方案,并且可以为每个音阶提供一个事件。这可以使用Observable.Interval
为节拍器构建天真,并Zip
将其连接到您的来源:
var source = GetInitSequence();
var trigger = Observable.Interval(TimeSpan.FromSeconds(5));
var triggeredSource = source.Zip(trigger, (s,_) => s);
triggeredSource.Subscribe(item => Console.WriteLine(DateTime.Now));
这将每5秒触发一次(在上例中),并按顺序提供原始项目。
这个解决方案的唯一问题是,如果你没有任何更多的源元素(比如说)10秒,当源元素到达时,它们将被立即发送出去,因为一些'触发'事件就在那里等他们。该场景的大理石图:
source: -a-b-c----------------------d-e-f-g
trigger: ----o----o----o----o----o----o----o
result: ----a----b----c-------------d-e-f-g
这是一个非常合理的问题。这里有两个问题可以解决它:
Rx IObservable buffering to smooth out bursts of events
A way to push buffered events in even intervals
提供的解决方案是主Drain
扩展方法和辅助Buffered
扩展。我已将这些修改为更简单(不需要Drain
,只需使用Concat
)。用法是:
var bufferedSource = source.StepInterval(TimeSpan.FromSeconds(5));
扩展方法StepInterval
:
public static IObservable<T> StepInterval<T>(this IObservable<T> source, TimeSpan minDelay)
{
return source.Select(x =>
Observable.Empty<T>()
.Delay(minDelay)
.StartWith(x)
).Concat();
}
答案 1 :(得分:1)
我知道这可能太简单了,但这会有效吗?
var intervaled = source.Do(x => { Thread.Sleep(100); });
基本上这只会在值之间产生最小延迟。太简单了?
答案 2 :(得分:1)
根据Enigmativity的回答,如果您只想通过TimeSpan延迟所有值,我无法理解为什么Delay
不是您想要的运算符
GetInitSequence()
.Delay(TimeSpan.FromSeconds(5)) //ideally pass an IScheduler here
.Subscribe(
item =>
{
Console.WriteLine(DateTime.Now);
// Process item
}
);
答案 3 :(得分:0)
Observable.Buffer怎么样?这应该将1s窗口中的所有事件作为单个事件返回。
var xs = Observable.Interval(TimeSpan.FromMilliseconds(100));
var bufferdStream = xs.Buffer(TimeSpan.FromSeconds(5));
bufferdStream.Subscribe(item => { Console.WriteLine("Number of events in window: {0}", item.Count); });
这可能是你要求的不清楚。你的代码应该做什么?看起来你只是通过为每个事件创建一个计时器来延迟。它还打破了observable的语义,因为下一个和完整可能在下一个之前发生。
请注意,这也仅在使用的计时器上准确无误。通常,定时器精确到最多16ms。
修改强>
您的示例变为,并且item包含窗口中的所有事件:
GetInitSequence()
.Buffer(TimeSpan.FromSeconds(5))
.Subscribe(
item =>
{
Console.WriteLine(DateTime.Now);
// Process item
}
);