如何使用RX做适当的Producer-Consumer模式

时间:2017-10-05 17:28:57

标签: c# system.reactive

我如何使用RX做生产者 - 消费者?

我找到this回答,它使用Java的调度程序来限制观察者上的concurent OnNext调用次数 - 所以我可以在C#中做类似的事情,但我确信有另一种方法。有吗?

此外,我发现标准主题不等待OnNext结束,它只是立即触发,这很奇怪。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

这完全是关于调度程序的,但历史上主题用于强制执行对.OnNext(...)的串行调用 - 问题是如果主题包含线程同步代码,那么许多非线程情况下的性能都会受到影响。所以主题更高效,但允许并发调用.OnNext(...)

但是,您可以通过调度程序或发布调用的一些适当用法来解决此问题。

让我们看看一些代码,从这些方法开始:

public void Foo()
{
    Console.WriteLine("Foo Start");
    Thread.Sleep(5000);
    Console.WriteLine("Foo End");
}

public void Bar()
{
    Console.WriteLine("Bar Start");
    Thread.Sleep(5000);
    Console.WriteLine("Bar End");
}

如果我现在写这个:

var subject = new Subject<Unit>();

subject.Subscribe(u => Foo());
subject.Subscribe(u => Bar());

subject.OnNext(Unit.Default);
subject.OnNext(Unit.Default);

我得到以下内容:

Foo Start
Foo End
Bar Start
Bar End
Foo Start
Foo End
Bar Start
Bar End

此代码在即时调度程序上运行 - 它必须等待每个.OnNext(...)调用完成才能继续。

使用此代码:

var subject = new Subject<Unit>();
var query = subject.ObserveOn(Scheduler.Default);

query.Subscribe(u => Foo());
query.Subscribe(u => Bar());

subject.OnNext(Unit.Default);
subject.OnNext(Unit.Default);

我现在得到:

Foo Start
Bar Start
Foo End
Foo Start
Bar End
Bar Start
Foo End
Bar End

调度程序现在可以自由使用线程池,因此它会同时调度调用。

现在,如果我们采用相同的代码,但.Publish()查询它会回到第一个行为:

var subject = new Subject<Unit>();
var query = subject.ObserveOn(Scheduler.Default).Publish();

query.Subscribe(u => Foo());
query.Subscribe(u => Bar());

query.Connect();

subject.OnNext(Unit.Default);
subject.OnNext(Unit.Default);

最后,如果我们回到原始代码,但使用EventLoopScheduler进行安排,那么我们将使用单个后台线程,以便再次进行系列调用。

var loop = new EventLoopScheduler();
var subject = new Subject<Unit>();
var query = subject.ObserveOn(loop);

query.Subscribe(u => Foo());
query.Subscribe(u => Bar());

subject.OnNext(Unit.Default);
subject.OnNext(Unit.Default);