Being Cellfish

Stuff I wished I've found in some blog (and sometimes did)

CCR tips and tricks - part 15

Change of Address
This blog has moved to blog.cellfish.se.

CCR tips and tricks - part 15

  • Comments 3

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.
  • This is an interesting idea - I have places in my application that I need to invoke plain-old-CLR-code from CCR thread and I have problems with it.

    The thing you didn't talk about here is that the order in which the CLR Thread Pool code runs is not predictable - this is something must be take into account when writing such code.

    Thank you,

    Ido.

  • I did not mention that the order the CLR thread pool execute things is not predictable since relying on that even with CCR is a big no-no IMHO. Even with CCR order is not predictable. In reality it is more predictable than using the CLR thread pool but I would never rely on it.

  • I agree - I didn't explain myself right I guess.

    I'm wrote my DAL with the CCR to be able to fetch multiple HTTP requests at the same time and have resource throttling just like you explain.

    One thing my DAL needs to do it notify other components that the data has changed.

    Since other components are not written in CCR I have to interact with them in non-CCR-safe way which means not block the CCR threads.

    Your post here show me a new, better way of doing it. The order problem is specific to my implementation.

    Thank you.

Page 1 of 1 (3 items)