我有多个(2-10)热观察者,每个都在给定的时间窗口内发射一个事件但不按顺序发射。
让我们说我有3个科目,为了说明它们按照任意顺序开火:
Subjects: sA, sB, sC
Order of time window 1:
sB, then sA, then sC
Order of time window 2:
sA, then sC, then sB
Order of time window 3:
sA, then sB, then sC
etc.
所以,如果我要进行合并,我会得到以下流:sB,sA,sC,sA,sC,sB,sA,sB,sC等。
现在,我的问题是:我想在每个时间窗口内强制执行事件的顺序。
1. sA
2. sB
3. sC
这将导致以下合并流:sA,sB,sC,sA,sB,sC,sA,sB,sC等。
窗口时间本身是未知的,但由于每个事件在每个窗口中只触发一次,我们可以假设一旦所有主体都被触发,窗口关闭并打开一个新窗口。
任何想法如何优雅地做到这一点?
扩展问题(不重要):observable彼此独立,所以我需要引入一个通用的静态方法,我可以同步每个observable的顺序。
public static IObservable<T> SynchronizeOrder<T>(IObservable<T> source, int order)
{
//return synchronized source
}
更新:我原来的问题中不明确的一点是,observable可以是不同的事件类型(这就是为什么我在我的例子中没有使用特定的事件数据类型)和因此我不想合并有序的可观察量。我希望有一种机制,我可以确保独立的可观察量按顺序同步。
myEventStream
.ObserveOn(TaskFactory)
.DoSomeExpensiveComputation()
.Order(2) //doesn't have to be an extension method
.Subscibe(computationResult=>
sharedRessourceWhereOrderMatters.Update(computationResult))
答案 0 :(得分:3)
使用可观察的连接执行此操作:
using System.Reactive;
using System.Reactive.Subjects;
using System.Reactive.Linq;
var a = new Subject<int>();
var b = new Subject<int>();
var c = new Subject<int>();
var test =
Observable.When(
a.And(b).And(c).Then((a1, b1, c1) => new int[] { a1, b1, c1 })
).SelectMany(arr => arr.ToObservable());
var sub = test.Subscribe(val => Console.Write("{0} ", val));
a.OnNext(1);
b.OnNext(2);
c.OnNext(3);
a.OnNext(1);
c.OnNext(3);
b.OnNext(2);
c.OnNext(3);
a.OnNext(1);
b.OnNext(2);
Console.ReadKey();
sub.Dispose();
输出结果为:
1 2 3 1 2 3 1 2 3
但请注意:
这并没有提供一种扩展方法,允许您以某种顺序注入特定的observable。您需要使用And
构建计划(可以逐步完成),然后最终执行它。 &#39;积累&#39;订单将是输出订单。
这不会看实际的窗口创建。但是,没有理由不能在某处注入可观察链。
每次所有三个observable都有值时,此解决方案将触发一次。您可能需要将其设置并在正确的位置将其拆除以获得所需的行为。
答案 1 :(得分:1)
对于具有相同范围的相同类型的可观察对象,yamens回答非常好。在我的情况下(扩展问题),可观察量可以是不同类型,并且可以在执行中的任何点同步订单。
所以这是我提出的简单解决方案:
private readonly object syncRoot = new object();
private int sourceCount;
private readonly SortedDictionary<int, Action> buffer = new SortedDictionary<int, Action>();
public IObservable<T> Order<T>(IObservable<T> source, int order)
{
return Observable.Create<T>(observer =>
{
lock (syncRoot)
sourceCount++;
source.Synchronize(syncRoot).Subscribe(item =>
{
buffer[order] = () => observer.OnNext(item);
if(buffer.Count == sourceCount)
{
foreach (var action in buffer.Values)
action();
buffer.Clear();
}
}, observer.OnError, observer.OnCompleted);
return () =>
{
lock (syncRoot)
sourceCount--;
};
});
}//end order