Preface
Talk about synchronicity, the day I post this article the CCR and DSS become a downloadable product. You can find the new product here. I suggest you watch this product, I suspect this to be a corner stone in concurrent .NET software development.
Introduction
I often revisit applications I've written to improve areas of the code with ideas and lessons I pick up over time. There seems to always be one primary goal and this is to improve performance. When it comes to improving performance there are many things you can do, but in the end you'll always look to multithreading. It's theoretically the simplest suggested concept but not always the easiest to implement. If you've ever run into resource sharing problems you'll know what I mean and although there are many articles on how to do it, it doesn't always mesh with every solution.
Sometime ago I came across something called the CCR, it was like magic code created by two wizards, Jeff Richter and George Chrysanthakopoulos. Part of the magic was to properly roll the syllables in Chrysanthakopoulos neatly off your tongue in one breath and when you get past that you'll see the light at the end of the multithreaded hallway of horrors. This managed DLL is packed with oodles of multithreaded fun and provides many levels of simplicity to common threading complexities. In other words, if you want to improve performance of your applications by implementing a multithreaded layer then you need to live and breathe the CCR. For some great background and fun, grab some popcorn and visit Jeff and George at this link.
Background
After watching the video cast, you should come away with some confidence and revelation along with some courage to start using the CCR. So you'll open up your latest project and... where do you start? Well one place you can start is by creating a simple asynchronous logger. Most applications I design have varying levels of logging for production diagnosis, but if you don't use a threaded model when utilizing your logger class then you've created blocking code and obvious room for improvement. So to get you started, I'll show you how to implement a CCR'd logger class that writes to a local file. There are many ways to log data but for this demo I'm using simple local logging. You will most likely be interested in this article; it will explain the many faces of the CCR.
Using the Code
The following code can be dropped into your application and be utilized right away and although basic, it can act as a replacement for any logging methods you currently implement.
The first thing we need to do is to new up something called a Dispatcher
, think of this as the thread "pool". Notice the "1
", this means we only want one thread handling these calls therefore all "posts" to the class will execute async but sequential. If you're writing to a SQL database you can try increasing this number but be aware that data may not arrive sequentially! When utilizing a dispatcher for other non sequential tasks, try increasing this number.
private static readonly Dispatcher _logDispatcher = new Dispatcher(1, "LogPool");
Secondly you'll want a DispatcherQueue
. The DQ manages your list of delegates to methods, methods you need to execute when needed.
private static DispatcherQueue _logDispatcherQueue;
Next you need a PORT, ports are like input queues. You'll "post" to ports to invoke your registered methods.
private static readonly Port<string> _logPort = new Port<string>();
Now for the class, don't forget to include the CCR in the directives!
using System;
using System.IO;
using System.Threading;
using Microsoft.Ccr.Core;
namespace CCRing_Demo
{
public static class DataLogger
{
private static readonly Dispatcher _logDispatcher = new Dispatcher(1,
ThreadPriority.Normal, false, "LogPool");
private static DispatcherQueue _logDispatcherQueue;
private static readonly Port<string> _logPort = new Port<string>();
private static string _logFileName;
private static void Init()
{
_logDispatcherQueue = new DispatcherQueue("LogDispatcherQueue",
_logDispatcher);
Arbiter.Activate(_logDispatcherQueue, Arbiter.Receive(true, _logPort,
WriteMessage));
_logFileName = "DMT_Message_Log_" + String.Format("{0:yyMMddHHmmss}",
DateTime.Now) + ".log";
}
private static void WriteMessage(string messageString)
{
using (var sw = File.AppendText(_logFileName))
sw.WriteLine("[{0:HH:mm:ss tt}] {1}", DateTime.Now, messageString);
}
public static void Log(string messageString)
{
if (String.IsNullOrEmpty(_logFileName))
Init();
_logPort.Post(messageString);
}
private static bool PendingJobs
{
get
{
return (_logDispatcher.PendingTaskCount > 0 ||
_logPort.ItemCount > 0) ? true : false;
}
}
public static void StopLogger()
{
while (PendingJobs){Thread.Sleep(100);}
_logFileName = null;
_logDispatcherQueue.Dispose();
}
}
}
Points of Interest
The CCR doesn't come with the latest version of Visual Studio. It's part of the Microsoft Robotics Studio but instead of downloading the entire studio, I've included the DLL above so you can add it as a reference to your project.
One thing you should notice from the code above is the lack of callback nesting that is involved here, truly a nice model.
Also, if you're using background threading your primary/initial thread will wait on the Dispatcher
, even if all Queued posts are completed. You can handle this in a number of ways, such as newing up the Distpatcher
without background threading but in this case you'll want to check to make sure all jobs are completed with PendingJobs
.
Although this class is fairly simplistic in its design and purpose, you should at least come out seeing the power the CCR holds with just a few lines of code. Step through the code and add some additional ports for fun. The more you understand the CCR the more you'll see how it can improve just about any application you write from here forward.
Happy CCRing!
History
- 29th October, 2008: Initial post