TPL vs Reactive Framework

什么时候会select在TPL上使用Rx还是两个框架是正交的?

据我所知,Rx主要是为了提供对事件的抽象,允许合成,但它也允许提供对asynchronous操作的抽象。 使用Createxx重载和Fromxxx重载并通过处置IDisposable返回取消。

TPL还通过任务和取消function提供了操作抽象。

我的困境是什么时候使用哪个和哪些场景?

Rx的主要目的不是提供对事件的抽象。 这只是其结果之一。 其主要目的是为collections提供可组合的推式模型。

反应框架(Rx)基于IObservable<T>IEnumerable<T>的math对偶。 因此,不是使用IEnumerable<T>从集合中“拉”项目,而是通过IObservable<T>将对象“推送”给我们。

当然,当我们真正去寻找可观察的来源时,事件和asynchronous操作是很好的候选。

react native框架自然要求multithreading模型能够观察可观察数据的来源并pipe理查询和订阅。 Rx实际上大量使用TPL来做到这一点。

所以,如果你使用Rx,你隐式使用TPL。

如果你希望直接控制你的任务,你可以直接使用TPL。

但是,如果您有希望观察和执行查询的数据源,则我会彻底推荐响应式框架。

我喜欢遵循一些准则:

  • 我处理的数据,我不是来自。 数据什么时候到达? 然后RX。
  • 我是否启动计算并需要pipe理并发? 然后TPL。
  • 我pipe理多个结果,需要根据时间select吗? 然后RX。

我喜欢Scott W的要点。 在Rx地图中join一些更具体的例子非常适合

  • 消耗stream量
  • 执行像Web请求一样的非阻塞asynchronous工作。
  • stream媒体事件(例如鼠标移动或服务总线消息types事件等.net事件)
  • 一起组成“事件stream”
  • Linq风格的操作
  • 公开API公开的数据stream

TPL似乎很好地映射到

  • 内部并行工作
  • 执行像Web请求一样的非阻塞asynchronous工作
  • 执行工作stream程和延续

我注意到IObservable(Rx)的一件事就是它变得普遍。 一旦进入你的代码库,无疑会通过其他接口暴露出来,它将最终出现在你的应用程序中。 我想这可能起初是可怕的,但现在大部分的团队都对Rx很满意,并且非常喜欢它为我们节省的工作量。

恕我直言,Rx将成为TPL的主stream图书馆,因为它已经在.NET 3.5,4.0,Silverlight 3,Silverlight 4和Javascript中得到了支持。 这意味着你必须学习一种风格,并且适用于很多平台。

编辑 :我已经改变了我的主意,Rx在TPL占主导地位。 他们解决不同的问题,所以不应该这样比较。 在.NET 4.5 / C#5.0中,asynchronous/等待关键字将进一步将我们与TPL(这是很好的)联系起来。 有关Rx vs事件与TPL等的深入讨论,请查看我在线书籍IntroToRx.com的第一章

2016年12月更新:如果你有30分钟的时间,我build议你阅读Joe Duffy的第一手资料,而不是我的猜测。 我认为我的分析很好,但是如果你发现这个问题,我强烈推荐你去看博客post,而不是这些答案,因为除了TPL和Rx.NET之外,他还包括MS研究项目(Midori,Cosmos)。

http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/


我想MS在.NET 2.0推出后犯了一个大错误。 他们从公司的不同部门同时引入了许多不同的并发pipe理API。

  • Steven Toub正在努力争取线程安全的原语来取代Event(它以Future<T>开始,并转化为Task<T>
  • MS研究有MIN-LINQ和无功扩展(Rx)
  • 硬件/embedded式有机器人cuntime(CCR)

与此同时,许多托pipeAPI团队试图与APM和Threadpool.QueueUserWorkItem()一起生活,不知道Toub是否会赢得他在“mscorlib.dll”中发布Future<T> / Task<T>战斗。 最后,它看起来像被对冲了,并且在mscorlib中发布了Task<T>IObservable<T> ,但是在mscorlib中不允许任何其他的Rx API(甚至是ISubject<T> )。 我认为这个对冲最终导致了大量的重复(更晚),浪费了公司内外的努力。

有关重复的信息,请参阅: Task vs. IObservable<Unit>Task<T>AsyncSubject<T>Task.Run()Observable.Start() 。 这只是冰山一angular。 但是在更高的层面上考虑:

  • StreamInsight – SQL事件stream,本机代码优化的,但使用LINQ语法定义的事件查询
  • TPL数据stream – 基于TPL构build,与Rx并行构build,针对调整线程并行性进行了优化,不擅长编写查询
  • Rx – 惊人的performance力,但充满危险。 用IEnumerable风格的扩展方法混合“热门”stream,这意味着你很容易永远阻塞First()在热stream上调用First()永远不会返回)。 调度限制(限制并行)是通过相当奇怪的SubscribeOn()扩展方法完成的,这些扩展方法奇怪地隐含着,很难正确地得到。 如果开始学习Rx保留很长时间,以了解所有避免的陷阱。 但是,如果组合复杂的事件stream,或者您需要复杂的过滤/查询,Rx实际上是唯一的select。

我不认为Rx有广泛采用的战斗机会,直到MS在mscorlib中发布ISubject<T> 。 这很令人伤心,因为Rx包含一些非常有用的具体(通用)types,比如TimeInterval<T>Timestamped<T> ,我认为它应该在Core / mscorlib中,像Nullable<T> 。 此外, System.Reactive.EventPattern<TEventArgs>

我会说TPL Dataflow涵盖了Rx中的专用子集。 数据stream是用于可能需要大量时间的数据处理,而Rx用于处理时间可以忽略的事件,例如鼠标位置,错误状态等。

例如:你的“订阅”处理程序是asynchronous的,你当时只需要一个执行程序。 有了Rx,你不得不阻止,因为Rx是asynchronous的,不会在许多地方以特殊的方式威胁asynchronous。

 .Subscribe(myAsyncHandler().Result) 

如果你没有阻塞,那么Rx会认为这个操作是完成的,而处理器仍然是asynchronous执行的。

你可能会认为,如果你这样做

 .ObserveOn(Scheduler.EventLoopSchedule) 

比问题解决了。 但是这会破坏你的.Complete()工作stream程,因为Rx会认为它在执行完成后就立即执行,并且不用等待asynchronous操作完成就退出应用程序。

如果您希望允许不超过4个并发asynchronous任务,则Rx不提供任何开箱即用的function。 也许你可以通过实现你自己的调度器,缓冲区等来破解一些东西。

TPL Dataflow在ActionBlock中提供了非常好的解决scheme。 它可以将同时执行的操作压缩到某个数字,并且理解asynchronous操作,因此调用Complete()并等待Completed将完成您期望的操作:等待所有正在进行的asynchronous任务完成。

TPL的另一个特点是“背压”。 假设您在处理例行程序时发现错误,需要重新计算上个月的数据。 如果您使用Rx订阅源代码,并且您的pipe道包含无限制的缓冲区(或ObserveOn),则比在几秒钟内您将耗尽内存,因为源代码将保持读取速度快于处理能够处理的速度。 即使你实现阻止消费者,你的源可能会阻塞呼叫,例如,如果源是asynchronous的。 在TPL你可以实现源代码

 while(...) await actionBlock.SendAsync(msg) 

这不会阻止源,但会等待处理程序过载。

总的来说,我发现Rx非常适合时间和计算轻的行为。 如果处理时间变得很大,那么你处于奇怪的副作用和深奥的debugging世界。

好消息是TPL数据stream块与Rx玩的很好。 他们有AsObserver / AsObservable适配器,你可以在需要的时候把它们粘在Rxpipe道的中间。 但是Rx有更多的模式和用例。 所以我的经验法则是从Rx开始,并根据需要添加TPL数据stream。

我做了一个Windows Phone应用程序。 开始与RX。 问题是,在某个时候,我使用了一个新版本的RX,猜测是什么? 很多函数都有“过时”属性。 那么我开始使用TPL,现在我有一个混合项目与两个。 我的build议是,如果你使用大量的asynchronousnetworking电话更好地使用TPL。 正如已经写的RX是使用TPL那么为什么不从一开始就使用TPL。