CCR tips and tricks - part 15

In part 14 I showed you how to work with an asynchronous API from CCR. Today we'll handle synchronous code form CCR. Since one of the most common ways to work with CCR is to use a dispatcher with one thread per core you do not want to block one thread by waiting on some synchronous, long running operation. Let's create a blocking operation first:

 private void BlockingMethod(int seconds)
{
    Thread.Sleep(TimeSpan.FromSeconds(seconds));
}

Calling this from a CCR thread would in most cases be devastating for performance of your code. If this method is called multiple times from different CCR tasks you could easily have all your CCR threads blocked in this method and that is probably not what you want. But we can fix this by using a feature of the DispatcherQueue. If you create a DispatcherQueue with no arguments it will not use a default dispatcher (with one thread per core), it will use the CLR thread pool! A good pattern to use if you do not know which dispatcher your code will be used with (this might be true if you have a library of functions for others to use) is demonstrated in this code:

   1: private IEnumerator<ITask> MethodThatNeedToCallBlockingMethodFromCcr(
  2:     Port<EmptyValue> donePort)
  3: {
  4:     if (!Thread.CurrentThread.IsThreadPoolThread)
  5:     {
  6:         using (var clrTaskQueue = new DispatcherQueue())
  7:         {
  8:             yield return
  9:                 Arbiter.ExecuteToCompletion(
 10:                     clrTaskQueue,
 11:                     new IterativeTask<Port<EmptyValue>>(
 12:                         donePort,
 13:                         MethodThatNeedToCallBlockingMethodFromCcr));
 14:             yield break;
 15:         }
 16:     }
 17:  
 18:     BlockingMethod(42);
 19:     donePort.Post(EmptyValue.SharedInstance);
 20: }

There are two reasons for the guard clause in the beginning that reschedules the task on a CLR thread;

  • You do not want to reschedule if already on a CLR thread since every time you schedule a task there is a small overhead.
  • You do not want to use the CLR thread pool for all of your CCR code since using the CLR thread pool dispatcher queue has a greater overhead for each task executed than using a standard CCR dispatcher queue with a more limited dispatcher. CCR has very small overhead when running with one thread per core.