In an effort to understand the basics behind CCR and see if I could play with the technology I created a console application with the following goals in mind:
Be able to queue up a “logger” type of request where a worker thread could take care of handling the processing of those requests.
In reality, I’m not doing much more than simply passing a string to a static class that uses CCR and its multithreading system to dump the output to the console. Nevertheless, the process is quite interesting and could be applied to a multitude of scenarios.
Observe the following Console/Program.cs file:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using Microsoft.Ccr.Core;
6: using System.Threading;
7: 8: namespace ConsoleApplication1
9: {10: class Program
11: {12: static void Main(string[] args)
13: {14: for (int i = 0; i < 10; i++)
15: ConsoleLogger.Log(i.ToString()); 16: 17: Console.WriteLine("Done queuing");
18: 19: ConsoleLogger.StopLogger(); 20: } 21: } 22: }No black magic here, the code is very readable. We create a loop (line 14) which takes care of requesting to our ConsoleLogger class to log 0-10 (line 15) and finally output “Done queuing” (line 17). In a real world we would expect 0-10 to be spit out and then see “Done queuing” but this is not what happens in this case, “Done queuing” appears first only to then see 0-10 each on its own line. Such is the magic of multithreading.
Let’s observe the source code for the Class/ConsoleLogger.cs
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using Microsoft.Ccr.Core;
6: using System.Threading;
7: 8: namespace ConsoleApplication1
9: {10: public static class ConsoleLogger
11: {12: //Create Dispatcher
13: private static readonly Dispatcher _logDispatcher = new Dispatcher(1,
14: ThreadPriority.Normal, false, "LogPool");
15: 16: //Create Dispatch Queue
17: private static DispatcherQueue _logDispatcherQueue;
18: 19: //Message Port
20: private static readonly Port<string> _logPort = new Port<string>();
21: 22: //Fields
23: private static bool _ready;
24: 25: private static void Init()
26: {27: _logDispatcherQueue = new DispatcherQueue("LogDispatcherQueue",
28: _logDispatcher);29: Arbiter.Activate(_logDispatcherQueue, Arbiter.Receive(true, _logPort,
30: WriteMessage)); 31: 32: _ready = true;
33: } 34: 35: private static void WriteMessage(string messageString)
36: { 37: Console.WriteLine(messageString); 38: } 39: 40: public static void Log(string messageString)
41: {42: if (!_ready)
43: Init(); 44: _logPort.Post(messageString); 45: } 46: 47: //Any thread tasks still running?
48: private static bool PendingJobs
49: { 50: get 51: {52: return (_logDispatcher.PendingTaskCount > 0 ||
53: _logPort.ItemCount > 0) ? true : false;
54: } 55: } 56: 57: //Since we are not using background threading we need to add this method to
58: //dispose the DQ for application end
59: public static void StopLogger()
60: {61: while (PendingJobs) { Thread.Sleep(100); }
62: _ready = false;
63: _logDispatcherQueue.Dispose(); 64: } 65: } 66: 67: }Note the declaration of (line 13) the dispatcher that will be used to dispatch requests to the CCR engine. Also observe the declaration of the dispatcher queue (line 17) and the message port (line 20).
Line 25 lists the Init code of our class that will set up the whole CCR settings so that the class uses multithreading internally to display our messages.
Line 35 demonstrates the actual method that performs the work, notice that the parameter accepted by this method is the exact same parameter as the one set up for the port on line 20. Therefore, you can assume that when you call the port the underlying engine will pass on this request to this method and simply pass the parameter along.
Line 40 shows the method that is used to actually insert some data in the port (queue) which will then be processed by the engine to be relayed to the worker method (line 35).
Line 48 shows an internal function that will allow us to stop the logger which will check if all the processing has completed for our class. We don’t want to exit the ConsoleApplication before all the work has been done. It also handles calling the Init method if the CCR engine has not be yet set up in this class.
Finally, line 59 defines the final method that is used to wait for all processing to be completed. We will check every second (pausing the thread for proper threading etiquette) and recheck until the CCR engine no longer has any messages to process and finally dispose of the Dispatcher Queue.
The code is not flawless but it properly illustrates the use of the CCR engine and its most basic capacities.
For more information on CCR and DSS check out the Microsoft page dedicated to this new and exciting technology.