我会说 TPL 数据流涵盖了 Rx 中专门的功能子集。 Dataflow 用于可能需要大量时间的数据处理,而 Rx 用于处理时间可以忽略不计的事件,例如鼠标位置、错误状态等。
示例:您的“订阅”处理程序是异步的,并且您当时希望不超过 1 个执行程序。使用 Rx 你必须阻止它,没有其他方法可以解决它,因为 Rx 与异步无关,并且在许多地方不会以特殊方式威胁异步。
.Subscribe(myAsyncHandler().Result)
如果你不阻塞,那么 Rx 会认为动作已经完成,而处理程序仍在异步执行。
如果你这样做,你可能会认为
.ObserveOn(Scheduler.EventLoopSchedule)
问题解决了。但这会破坏您的 .Complete() 工作流程,因为 Rx 会认为它在安排执行后立即完成,您将退出应用程序而无需等待异步操作完成。
如果您希望允许不超过 4 个并发异步任务,那么 Rx 不会提供任何开箱即用的功能。也许您可以通过实现自己的调度程序、缓冲区等来破解某些东西。
TPL Dataflow 在 ActionBlock 中提供了非常好的解决方案。它可以将同时操作限制到一定数量,并且它确实理解异步操作,因此调用 Complete() 并等待 Completed 将完全符合您的预期:等待所有正在进行的异步任务完成。
TPL 的另一个功能是“背压”。假设您在处理程序中发现了一个错误,需要重新计算上个月的数据。如果您使用 Rx 订阅源,并且您的管道包含无限缓冲区或 ObserveOn,那么您将在几秒钟内耗尽内存,因为源将继续读取速度超过处理可以处理的速度。即使您实现了阻塞消费者,您的源也可能会受到阻塞调用的影响,例如,如果源是异步的。在 TPL 中,您可以将源代码实现为
while(...)
await actionBlock.SendAsync(msg)
在处理程序重载时不会阻塞源代码。
总的来说,我发现 Rx 非常适合时间和计算量小的动作。如果处理时间变得很长,那么您将处于奇怪的副作用和深奥的调试世界中。
好消息是 TPL 数据流块与 Rx 配合得非常好。它们具有 AsObserver/AsObservable 适配器,您可以在需要时将它们粘贴在 Rx 管道的中间。但是 Rx 有更多的模式和用例。所以我的经验法则是从 Rx 开始,根据需要添加 TPL 数据流。