Rx-几个生产者/一个消费者

时间:2014-01-21 20:36:27

标签: system.reactive

一直试图谷歌这个但是有点卡住了。

假设我们有一个触发事件的类,并且该事件可能同时被多个线程触发。

使用Observable.FromEventPattern,我们创建一个Observable,并订阅该事件。 Rx究竟如何管理多个同时被触发的事件?假设我们在不同的线程上快速连续发射了3个事件。它是否在内部排队,然后为每个队列同步调用Subscribe委托?假设我们订阅了一个线程池,我们是否仍然可以保证订阅将在时间上单独处理?

继续之后,让我们说对于每个事件,我们想要执行一个动作,但它是一个可能不是线程安全的方法,所以我们一次只希望一个线程在这个方法中。现在我看到我们可以使用EventLoop Scheduler,大概我们不需要对代码实现任何锁定?

另外,观察当前线程是一个选项吗?当前线程是事件被触发的线程,还是订阅设置的事件?即当前线程是否保证总是相同,或者可能有2个线程同时运行在方法中?

THX

PS:我把一个例子放在一起,但我似乎总是在我的订阅方法中使用相同的线程,即使我在线程池中观察到了这一点,这令人困惑:S

PSS:通过做一些实验,似乎如果没有指定Scheduler,那么RX将只在事件被触发的任何线程上执行,这意味着它同时处理几个。一旦我引入了一个调度程序,无论调度程序的类型是什么,它总是连续运行。奇怪:S

3 个答案:

答案 0 :(得分:2)

根据Rx Design Guidelines,一个观察者不应该同时调用观察者的OnNext。在进行下一次呼叫之前,它将始终等待当前呼叫完成。所有Rx方法都遵循此约定。更重要的是,他们认为你也尊重这个惯例。当您违反此条件时,您可能会遇到Observable行为中的微妙错误。

如果您的源数据不符合此约定(即它可以同时生成数据),那么它们会提供Synchronize

Observable.FromEventPattern假设您不会触发并发事件,因此不会阻止并发下游通知。如果您打算同时从多个线程触发事件,请使用Synchronize()作为FromEventPattern后执行的第一个操作:

// this will get you in trouble if your event source might fire events concurrently.
var events = Observable.FromEventPattern(...).Select(...).GroupBy(...);

// this version will protect you in that case.
var events = Observable.FromEventPattern(...).Synchronize().Select(...).GroupBy(...);

现在所有下游运营商(以及最终的观察者)都受到保护,不受Rx设计指南所承诺的并发通知的影响。 Synchronize使用简单的互斥锁(又称lock语句)。没有奇特的排队或任何东西。如果一个线程尝试引发一个事件而另一个线程已经引发它,则第二个线程将阻塞,直到第一个线程完成。

答案 1 :(得分:0)

除了使用Synchronize的建议之外,阅读Intro to Rx section on scheduling and threading也许值得。它涵盖了不同的调度程序及其与线程的关系,以及ObserveOn和SubscribeOn之间的差异等。

答案 2 :(得分:0)

如果你有几个生产者,那么有一些RX方法可以用线程安全的方式组合它们

用于将相同类型事件的流组合到单个流中

Observable.Merge

使用选择器将不同类型事件的流组合成单个流,以将每个流上的最新值转换为新值。

Observable.CombineLatest

例如合并来自不同来源的股票价格

IObservable<StockPrice> source0;
IObservable<StockPrice> source1;
IObservable<StockPrice> combinedSources = source0.Merge(source1);
每次点击时

或在当前位置创建气球

IObservable<ClickEvent> clicks;
IObservable<Position> position;
IObservable<Balloons> balloons = clicks
    .CombineLatest
    ( positions
    , (click,position)=>new Balloon(position.X, position.Y)
    );

为了使这与你的问题特别相关,你说有一个类结合了来自不同线程的事件。然后我将使用Observable.Merge来组合各个事件源并将其作为主类的Observable公开。

顺便说一句,如果您的线程实际上是触发事件的任务,说它们已经在这里完成了一个有趣的模式

IObservable<Job> jobSource;
IObservable<IObservable<JobResult>> resultTasks = jobSource
    .Select(job=>Observable.FromAsync(cancelationToken=>DoJob(token,job)));
IObservable<JobResult> results = resultTasks.Merge();

正在发生的事情是你正在获得一系列工作。从你正在创建异步任务流(尚未运行)的作业中。然后,合并运行任务并收集结果。它是mapreduce算法的一个例子。如果observable取消订阅(即取消),取消令牌可用于取消运行异步任务