We have recently ported the system from RX 1.11111 to RX 2.0 and discovered this problem. We use an EventLoopScheduler for ObserveOn like this:
IDisposable s
We ran into the same problem and ended up doing the following to dispose the EventLoopScheduler without exceptions:
scheduler.Schedule(() => scheduler.Dispose());
If you properly dispose all subscriptions before you do this (which you say you did), the Dipose() call is the last scheduled operation and all other pending operations can complete before Dispose is called.
To make this more robust/reusable, you could create your own IScheduler implementation wrapping the EventLoopScheduler that would delegate all operations to it + implement Dispose as shown above. On top of that, you could implement guards in the Schedule methods to prevent scheduling an action after Dispose has been called (e.g. if you forget to unsubscribe some observer).