我有以下课程的集合:
public class Event
{
public DateTimeOffset Timestamp;
public object Data;
}
我想创建IObservable<Event>
,其中每个项目在将来Timestamp
时发布。这可能是Observable.Delay
还是我必须编写自己的IObservable<T>
实现?
我会提到这个结构就像一个日志文件。可能有数以万计的Event
个项目,但每秒只能发布1-2个。
答案 0 :(得分:5)
事实证明,使用Observable.Delay
重载变得非常简单:
//given IEnumerable<Event> events:
var observable = events.ToObservable().Delay(ev => Observable.Timer(ev.Timestamp));
答案 1 :(得分:1)
虽然我的第一个答案是按预期工作,但创建可观察序列的性能对于成千上万的事件并不理想 - 您需要支付大量的初始化成本(在我的机器上按10秒的顺序)。
为了提高性能,利用已经排序的数据特性,我实现了自定义IEnumerable<Event>
循环事件,在它们之间产生和休眠。使用此IEnumerable
,可以轻松调用ToObservable<T>
并按预期工作:
IObservable<Event> CreateSimulation(IEnumerable<Event> events)
{
IEnumerable<Event> simulation()
{
foreach(var ev in events)
{
var now = DateTime.UtcNow;
if(ev.Timestamp > now)
{
Thread.Sleep(ev.Timestamp - now);
}
yield return ev;
}
}
return simulation().ToObservable();
}
答案 2 :(得分:0)
似乎Rx库缺少通过延迟枚举和时移其元素将IEnumerable<T>
转换为IObservable<T>
的机制。下面是一个自定义实现。这个想法是Zip
可以与某个主题枚举的源,并通过在适当的时候向该主题发送OnNext
通知来控制枚举。
/// <summary>Converts an enumerable sequence to a time shifted observable sequence,
/// based on a time selector function for each element.</summary>
public static IObservable<T> ToObservable<T>(
this IEnumerable<T> source,
Func<T, DateTimeOffset> dueTimeSelector,
IScheduler scheduler = null)
{
scheduler ??= Scheduler.Default;
return Observable.Defer(() =>
{
var subject = new BehaviorSubject<Unit>(default);
return subject
.Zip(source, (_, x) => x)
.Delay(x => Observable.Timer(dueTimeSelector(x), scheduler))
.Do(_ => subject.OnNext(default));
});
}
之所以选择BehaviorSubject
是因为它具有初始值,因此可以自然地使车轮运动。
Observable.Defer
运算符用于防止多个订阅共享同一状态(在这种情况下为subject
),并且彼此干扰。有关此here的更多信息。
用法示例:
IEnumerable<Event> events = GetEvents();
IObservable<Event> observable = events.ToObservable(x => x.Timestamp);