TPL与反应框架

时间:2010-03-30 03:46:38

标签: c# .net task-parallel-library system.reactive

何时会选择使用Rx而不是TPL,或者2个框架是否正交?

据我所知,Rx主要用于提供事件的抽象并允许组合,但它也允许提供异步操作的抽象。 使用Createxx重载和Fromxxx重载并通过处理返回的IDisposable取消。

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

我的困境是何时使用哪种情况以及在什么情况下?

5 个答案:

答案 0 :(得分:44)

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

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

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

反应式框架自然需要一个多线程模型才能观察可观察数据的来源并管理查询和订阅。 Rx实际上大量使用TPL来做到这一点。

因此,如果您使用Rx,则隐式使用TPL。

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

但是如果您有想要观察的数据来源并执行查询,那么我会彻底推荐反应框架。

答案 1 :(得分:24)

我喜欢遵循的一些指导原则:

  • 我处理的是我不是来自的数据。什么时候到达的数据?然后RX。
  • 我是原创计算还是需要管理并发?然后是TPL。
  • 我管理多个结果,需要根据时间选择吗?然后RX。

答案 2 :(得分:12)

我喜欢Scott W的要点。举几个具体的例子 Rx很好地映射到

  • 消费流
  • 执行非阻止异步工作,例如网络请求。
  • 流媒体事件(.net事件,如鼠标移动或服务总线消息类型事件)
  • 一起撰写活动的“流”
  • Linq style operations
  • 公开来自公共API的数据流

TPL似乎很好地映射到

  • 工作的内部并行化
  • 执行非阻止异步工作,例如网络请求
  • 执行工作流程和延续

我注意到IObservable(Rx)的一个原因是它变得无处不在。一旦进入你的代码库,因为它无疑将通过其他接口公开,它最终将出现在你的应用程序中。我想这一开始可能会让人感到害怕,但是大部分团队现在对Rx非常满意并且喜欢它为我们节省的工作量。

IMHO Rx将成为TPL的主流库,因为它已经在.NET 3.5,4.0,Silverlight 3,Silverlight 4和Javascript中得到支持。这意味着您必须学习一种风格,它适用于许多平台。

编辑:我已经改变了主意Rx在TPL上占主导地位。他们解决了不同的问题,所以不应该像这样进行比较。使用.NET 4.5 / C#5.0,async / await关键字将进一步将我们与TPL联系起来(这很好)。有关Rx与事件与TPL等的深入讨论,请查看我的在线图书first chapterIntroToRx.com

答案 3 :(得分:10)

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

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


我认为MS在.NET 2.0问世之后犯了一个大错误。他们从公司的不同部门同时引入了许多不同的并发管理API。

  • Steven Toub正在努力争取线程安全的原语取代Event(以Future<T>开头并变成Task<T>
  • MS Research有MIN-LINQ和Reactive Extensions(Rx)
  • 硬件/嵌入式有robotics cuntime (CCR)

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

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

  • StreamInsight - SQL事件流,本机代码优化,但使用LINQ语法定义的事件查询
  • TPL数据流 - 基于TPL构建,与Rx并行构建,针对调整线程并行性进行了优化,不擅长撰写查询
  • Rx - 惊人的表现力,但充满了危险。使用IEnumerable样式扩展方法混合“热”流,这意味着您可以非常轻松地永久阻止(在热流上调用First()永远不会返回)。调度限制(限制并行性)是通过相当奇怪的SubscribeOn()扩展方法完成的,这些方法非常隐含且难以正确。如果开始学习Rx预留很长时间来学习所有要避免的陷阱。但是,如果编写复杂的事件流或者需要复杂的过滤/查询,Rx实际上是唯一的选择。

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

答案 4 :(得分:7)

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

示例:您的&#34;订阅&#34; handler是异步的,你当时想要的执行程序不超过1个。使用Rx,你必须阻止它,没有别的方法,因为Rx是异步不可知的,并且不会在许多地方以特殊方式威胁异步。

.Subscribe(myAsyncHandler().Result)

如果你没有阻止,那么当处理程序仍在异步执行时,Rx会认为该操作已完成。

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

.ObserveOn(Scheduler.EventLoopSchedule)
问题解决了。但是这会打破你的.Complete()工作流程,因为Rx会认为它一旦安排执行就会完成,你将退出应用程序而不等待异步操作完成。

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

TPL Dataflow在ActionBlock中提供了非常好的解决方案。它可以将同时操作限制为特定数量,并且它确实理解异步操作,因此调用Complete()并等待Completed将完全按照您的预期执行:等待所有正在进行的异步任务完成。

TPL的另一个特点是&#34;背压&#34;。假设您发现处理程序中存在错误,需要重新计算上个月的数据。如果您使用Rx订阅源代码,并且您的管道包含无界缓冲区或ObserveOn,那么您将在几秒钟内耗尽内存,因为源将保持读取速度超过处理可处理的速度。即使您实现了阻塞使用者,您的源也可能会遇到阻塞调用,例如,如果源是异步的。在TPL中,您可以将源实现为

while(...)
    await actionBlock.SendAsync(msg)
在处理程序重载时,

不会阻止源。

总的来说,我发现Rx非常适合时间和计算都很轻松的动作。如果处理时间变得充实,那么您就会处于奇怪的副作用和深奥调试的世界。

好消息是TPL Dataflow块对Rx起到了很好的作用。它们具有AsObserver / AsObservable适配器,您可以在需要时将它们粘贴在Rx管道的中间。但是Rx有更多的模式和用例。所以我的经验法则是从Rx开始并根据需要添加TPL数据流。