When would one choose to use Rx over TPL or are the 2 frameworks orthogonal?
From what I understand Rx is primarily intended to provide an abstraction over events and allow composition but it also allows for providing an abstraction over async operations. using the Createxx overloads and the Fromxxx overloads and cancellation via disposing the IDisposable returned.
TPL also provides an abstraction for operations via Task and cancellation abilities.
My dilemma is when to use which and for what scenarios?
The main purpose of Rx is not to provide an abstraction over events. This is just one of its outcomes. Its primary purpose is to provide a composable push model for collections.
The reactive framework (Rx) is based on IObservable<T>
being the mathematical dual of IEnumerable<T>
. So rather than "pull" items from a collection using IEnumerable<T>
we can have objects "pushed" to us via IObservable<T>
.
Of course, when we actually go looking for observable sources things like events & async operations are excellent candidates.
The reactive framework naturally requires a multi-threaded model to be able to watch the sources of observable data and to manage queries and subscriptions. Rx actually makes heavy use of the TPL to do this.
So if you use Rx you are implicitly using the TPL.
You would use the TPL directly if you wish direct control over your tasks.
But if you have sources of data that you wish to observe and perform queries against then I thoroughly recommend the reactive framework.
Some guidelines I like to follow:
Update, December 2016: If you have 30 minutes, I recommend you read Joe Duffy's first-hand account instead of my speculation. I think my analysis holds up well, but if you've found this question I highly recommend you see the blog post instead of these answers because in addition to TPL vs Rx.NET he also covers MS research projects (Midori, Cosmos).
http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/
I think MS made a big mistake over-correcting after .NET 2.0 came out. They introduced many different concurrency management APIs all at the same time from different parts of the company.
Future<T>
and turned into Task<T>
)In the meantime many managed API teams were trying to live with APM and Threadpool.QueueUserWorkItem()
, not knowing if Toub would win his fight to ship Future<T>
/Task<T>
in mscorlib.dll. In the end it looks like they hedged, and shipped both Task<T>
and IObservable<T>
in mscorlib, but didn't allow any other Rx APIs (not even ISubject<T>
) in mscorlib. I think this hedge ended up causing a huge amount of duplication (more later) and wasted effort inside and outside the company.
For duplication see: Task
vs. IObservable<Unit>
, Task<T>
vs. AsyncSubject<T>
, Task.Run()
vs. Observable.Start()
. And this is just the tip of the iceberg. But at a higher level consider:
IEnumerable
-style extension methods, which means you very easily block forever (calling First()
on a hot stream never returns). Scheduling limits (limiting parallelism) is done via rather odd SubscribeOn()
extension methods, which are weirdly implicit and hard to get right. If starting to learn Rx reserve a long time to learn all the pitfalls to avoid. But Rx is really the only option if composing complex event streams or you need complex filtering/querying.I don't think Rx has a fighting chance at wide adoption until MS ships ISubject<T>
in mscorlib. Which is sad, because Rx contains some very useful concrete (generic) types, like TimeInterval<T>
and Timestamped<T>
, which I think should be in Core/mscorlib like Nullable<T>
. Also, System.Reactive.EventPattern<TEventArgs>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With