Wednesday, March 29, 2017

SubscribeOn / ObserveOn

Once you have an instance of an IObservable<T> object, Reactive Extensions (Rx) provides at least two points where you can choose an IScheduler which will determine the threading context under which the later operations will run. Those operations can be broken down into two phases: subscription and observation.

Subscription refers to the operations that modify the behaviours of an IObservable<T> (e.g. Throttle, Merge, Concat) The IScheduler for the subscription phase is assigned by the SubscribeOn() method.

Observation refers to the callback operations that are invoked as the result of observing an IObservable<T> (e.g. OnNext, OnCompleted, OnError) The IScheduler for the observation phase is assigned by the ObserveOn() method.

While not the subject of this post, you can also choose an IScheduler implementation to convert any IEnumerable<T> to an IObservable<T> with the ToObservable method. This is relevant when the IEnumerable<T> has the potential to take a long time to be enumerated.

Saturday, March 04, 2017

6 Simple Rules for Async/Await

  1. If a method A calls another method B that returns a Task (or Task<T>) then the calling method does not block on the completion of that task, unless it either:
    1. awaits that task, or
    2. waits on the result of that task
  2. The calling method cannot await a task unless it is declared with the async modifier, which causes the compiler to build a state machine (similar to the IEnumerable iterator) for that awaitable method.
  3. In point 1 above, the behaviour is independent of whether or not B is marked with the async keyword
  4. It is this non-blocking behaviour of A that allows the calling thread to proceed past the point where B is invoked. It's even possible for the thread's call stack to unwind, and for the thread to go back to reading the Windows message queue if it was the UI thread.
  5. The compiler will only warn you "because this call is not awaited ..." if B was marked with async.
  6. It is expected that A will do something with the Task returned by B; at the very least there should be some code to check that the Task did not throw any exceptions. If - instead of B - we have an async method C that returns void then we do not present A with any opportunity to monitor the completion of C. Unobserved exceptions thrown during the execution of C could indicate corrupted program state and can be configured to terminate the application in much the same way that an unhandled exception in synchronous code can unwind a stack fully and terminate the process.