Solution 1:

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.

Solution 2:

Some guidelines I like to follow:

  • Am I dealing with data that I don't originate. Data which arrives when it pleases? Then RX.
  • Am I originating computations and need to manage concurrency? Then TPL.
  • Am I managing multiple results, and need to choose from them based on time? Then RX.

Solution 3:

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.

  • Steven Toub was pushing hard for thread-safe primitives to replace Event (which started as Future<T> and turned into Task<T>)
  • MS Research had MIN-LINQ and Reactive Extensions (Rx)
  • Hardware/Embedded had robotics cuntime (CCR)

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:

  • StreamInsight - SQL event streams, native-code-optimized, but event queries defined using LINQ syntax
  • TPL Dataflow - built on TPL, built in parallel to Rx, optimized for tweaking threading parallelism, not good at composing queries
  • Rx - Amazing expressiveness, but fraught with peril. Mixes 'hot' streams with 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>.