是否可以使用ReactiveExtensions来实现以下目标;
两个Observable,一个是" High"优先级和其他"低"
将两个Observable合并为一个,然后可以订阅,意图是这个结果Observable将始终在任何低优先级项目之前发出高优先级项目。
据我所知,使用两个ConcurrentQueue
个集合可以更简单地实现这一点;
return this.highPriorityItems.TryDequeue(out item)
|| this.lowPriorityItems.TryDequeue(out item);
但是这种方法存在的问题就像没有"可订阅"与Observable相同的方式(所以一旦队列耗尽,处理就会结束而没有太多额外的guff将其推送到任务中)。
此外,我有兴趣在队列上应用一些额外的过滤功能,例如限制和"不同,直到更改",所以Rx在这里看起来很自然。
答案 0 :(得分:4)
您所描述的当然是优先队列。
Rx是关于事件的 streams ,而不是队列。当然,队列 在Rx中使用了很多 - 但它们不是一流的概念,更多是Rx概念实现细节的一部分。
我们需要队列的一个很好的例子是处理一个缓慢的观察者。事件在Rx中按顺序分派,如果事件的到达速度快于观察者可以处理的事件,那么它们必须与该观察者排队。如果有很多观察者,那么必须保持多个逻辑队列,因为观察者可能以不同的速度前进 - 而且Rx选择不让它们保持锁定步骤。
“背压”是观察者向观察者提供反馈的概念,以便允许机制处理更快的可观察的压力 - 例如混淆或节流。 Rx没有引入背压的一流方法 - 唯一内置意味着可观察到的监控观察者是通过OnNext
的同步性质。任何其他机制都需要带外。你的问题直接与背压有关,因为它仅在慢速观察者的情况下才有意义。
我提到这一切是为了证明我声称Rx不是提供您正在寻找的优先级调度的一个很好的选择 - 实际上,一流的排队机制似乎更合适。
要解决手头的问题,您需要在自定义运算符中自行管理优先级排队。重申问题:你所说的是,如果事件在观察者处理OnNext
事件期间到达,这样就会产生要调度的事件,而不是Rx使用的典型FIFO队列,你想根据一些优先级发送。
需要注意的是,根据Rx如何不将多个观察者保持在锁定步骤的精神,并发观察者可能会以不同的顺序看到事件,这对您来说可能是也可能不是问题。您可以使用Publish
之类的机制来获得订单一致性 - 但您可能不希望这样做,因为事件传递的时间在这种情况下会变得非常不可预测且效率低下。
我确信有更好的方法可以做到这一点,但这里是基于优先级队列的交付的一个示例 - 您可以使用更好的方式扩展它以适用于多个流和优先级(甚至每个事件的优先级)队列实现(例如基于b树的优先级队列)但我选择保持这相当简单。即便如此,请注意代码必须解决的大量问题,包括错误处理,完成等等。我已经做出选择,以确定何时发出这些信号,确实存在大量其他有效选择。
总而言之,这种实现肯定会让我不再使用Rx。它足够复杂,无论如何都可能存在错误。正如我所说,可能有更简洁的代码(特别是考虑到我付出的最小努力!),但概念,无论实现如何,我对这个想法感到不舒服:
public static class ObservableExtensions
{
public static IObservable<TSource> MergeWithLowPriorityStream<TSource>(
this IObservable<TSource> source,
IObservable<TSource> lowPriority,
IScheduler scheduler = null)
{
scheduler = scheduler ?? Scheduler.Default;
return Observable.Create<TSource>(o => {
// BufferBlock from TPL dataflow is used as it is
// handily awaitable. package: Microsoft.Tpl.Dataflow
var loQueue = new BufferBlock<TSource>();
var hiQueue = new BufferBlock<TSource>();
var errorQueue = new BufferBlock<Exception>();
var done = new TaskCompletionSource<int>();
int doneCount = 0;
Action incDone = () => {
var dc = Interlocked.Increment(ref doneCount);
if(dc == 2)
done.SetResult(0);
};
source.Subscribe(
x => hiQueue.Post(x),
e => errorQueue.Post(e),
incDone);
lowPriority.Subscribe(
x => loQueue.Post(x),
e => errorQueue.Post(e),
incDone);
return scheduler.ScheduleAsync(async(ctrl, ct) => {
while(!ct.IsCancellationRequested)
{
TSource nextItem;
if(hiQueue.TryReceive(out nextItem)
|| loQueue.TryReceive(out nextItem))
o.OnNext(nextItem);
else if(done.Task.IsCompleted)
{
o.OnCompleted();
return;
}
Exception error;
if(errorQueue.TryReceive(out error))
{
o.OnError(error);
return;
}
var hiAvailableAsync = hiQueue.OutputAvailableAsync(ct);
var loAvailableAsync = loQueue.OutputAvailableAsync(ct);
var errAvailableAsync =
errorQueue.OutputAvailableAsync(ct);
await Task.WhenAny(
hiAvailableAsync,
loAvailableAsync,
errAvailableAsync,
done.Task);
}
});
});
}
}
示例用法:
void static Main()
{
var xs = Observable.Range(0, 3);
var ys = Observable.Range(10, 3);
var source = ys.MergeWithLowPriorityStream(xs);
source.Subscribe(Console.WriteLine, () => Console.WriteLine("Done"));
}
这将首先打印出ys
的元素,表明它们具有更高的优先级。
答案 1 :(得分:2)
您需要花时间考虑这样的问题。在上面的评论中,您将讨论用户通知。在我看来,你想要的是一种说法是这样的:显示最近的通知,除非有高优先级通知,在这种情况下显示。
气泡图将更容易对此进行推理。一个字符是一秒:
High : ---------3---5-6
Low : 1--2-------4----
Result: 1--2-----3---5-6
这是你的想法吗?您想缓冲消息并在以后显示它们吗?就像在这种情况下一样,消息5只能在2秒内可见吗?