This article is something that I’ve been meaning to do for quite some time and I’m glad that I chose this as my first article to be published on Code Project. So, on the one hand, I am quite glad to present some of the things that I’ve been using and will probably continue to use on some of my personal projects and on the other hand I’m quite nervous to put these things out there on the web where others can see it and, hopefully, will review it in a positively critical manner. This article is about a very simple little object that I use whenever I have the need for a Memory Queue in a multi-threaded environment or when I would like to assign some work to specialized thread handlers. I will show how it works by way of some very simple and straightforward Unit Tests written using the N-Unit framework and then I’ll use it to implement an handler system as well as a Logger that writes to a text file at incredible speeds without affecting my main processing thread. Hopefully, you’ll see some cases where you can also use it to make things a bit simpler for yourself.
- Scenario 1 – Thread Queue
- Scenario 2 – Logger
- Using the code
- Solution Structure
- Solution Explorer
- Useful Links
I hope the above will be sufficient to give you a good idea on how to use the Memory Queue.
I’ve always needed a way to assign work to various threads without having to worry too much about synchronization and also to remove the load of doing the actual work from my main thread(s). Of course some of you out there might think that I could use something like an asynchronous delegate to do what I’m going to show you or I could use something like Reactive Extensions and while I’ll probably agree with you on some instances, I will also disagree with you on others. I like keeping things simple as far as I can and when you build a framework or an application, you’ll soon realize that things can be simpler by using some well-defined patterns or some third party tool sets and frameworks, but at the same time I’ve always been someone that likes doing my own thing when it comes to code seeing that I’m usually the one that would have to support the product for some time to come. While third party toolsets are a big help in a lot of what we do nowadays, it also has the ability to introduce constraints on the way we want to do things and that doesn’t include the overhead of the learning cycle or the process. If I have a simpler faster way to write my code in a consistent manner, then why not, right? So let’s get onto the scenarios…
Scenario 1 – Thread Queue
In this scenario, I’ll take the Memory Queue and write a simple and efficient Thread Queue that will allow you to implement the Command Pattern which will allow us to have a nice easy separation of the Business Value that you want to add without affecting the Main Process.
Scenario 2 – Logger
In this scenario, I’ll implement a small logging framework that uses the above Thread Queue where the Command that I’ll be implementing will be writing to a delimited file. This will allow you to provide the Logger with a specific formatter which will format the data the way you want it before writing it to a delimited file format. This Logger will also zip the file when it reaches a specific number of lines in the log file, which will allow us to keep the size of our logs manageable. One thing that I have to note here is that because the actual file handling and writing gets done on a different thread from our Business Process, it doesn’t interfere as much as it would if we had inline logging. Another thing is that this logger is extremely light and fast, while allowing you to add more Command Types and making it quite easy to configure.
Using the Code
As a short overview to the solution above, you’ll see that there are three projects that contain the code that we’re interested in and two test projects to test the code. I’ll be breaking the projects down in the coming sections to explain a bit about what each project does. The three implementation projects are called:
Yakiloo.Logging. My tests are implemented in the two projects called:
Yakiloo.Logging.Tests. To get the best overview on how to use the implementation classes, you can have a look at the Tests written for the classes. Even though the tests are not designed to show you all the different scenarios that can be applied to those classes, they do give you a nice overview of how they work and how they should and can be used.
This class is an implementation of a Memory Queue which I use as a way to queue work that I want different threads to do. This allows me to reuse threads without having to create new ones, which as we all know is a very heavy operation. The Memory Queue allows for fast writes and fast reads and is written in a Thread-Safe way. It uses the generic
LinkedList in the
System.Collections.Generic namespace in the .NET Framework. The reason for this can be found in the way the
LinkedList is implemented whereby it allows us to access items within the
List quite easily by using properties such as “
First” and “
Last” and methods that allows us to add items to the list such as “
AddLast”. If you start drilling down into the implementation of the Linked List, then you’ll see that the items contained inside the list are not the actual type that you add, but rather items optimized for reading and writing in an asynchronous manner without affecting the rest of the
List. I would suggest you do some of your own reading on the type to get a better understanding of what makes a Linked List different from other Lists or Collections. For more information on the Linked List, you can read about it on the MSDN page here. The next item you should be aware of is the way I use the
System.Threading.Monitor class as well as how I use the various locking objects I’ve implemented. The
Monitor class allows me to do various things which you can read up about on the MSDN page here. It is an extremely powerful class to use and if you’re unfamiliar with Threading I would suggest reading Sacha Barber’s articles about threading where he does an extremely good job of explaining the various things you need to know about threading and the classes that you can use in your implementations. You can find his first article of the range here and I would suggest reading this your first stop if you’re interested in these powerful concepts. He does a great job at explaining how you can get started as well as how the more advanced topics work.
So, I needed a way to pass some work to different threads in to be processed when they had an opening. With the help of the Memory Queue and the Command Pattern, I was able to write the Thread Queue that I’ll be explaining in this section. This class encapsulates the Memory Queue in the way that it works where it allows you to add items to be processed to the Memory Queue, but by way of the Command Pattern allows you to write specialized classes, i.e., commands to apply to the items. This means that you don’t get access to read from the queue seeing that it contains thread(s) that reads from the queue and executes the command that was assigned to the queue. If you want to think about this in a more visual manner, you can look at the image below:
In this image, you’ll see that we have multiple Business Processes (BP) adding items to the queue to be processed by the Command Object. Once it hits the Queue, you’ll see
Monitor.Pulse() methods get executed, which lets the first waiting thread know that there is work to be done. At this stage, the waiting thread will then come out of wait mode and take the next first item on the queue and process it by executing the Command object’s
Execute method passing the Work Item as a parameter to the
Execute method. The image below shows this a little bit more clearly, I hope:
As you can see, there are multiple threads that executed the
Dequeue method, which will sit in a
Wait state until signaled that there is work to be done or that the
Dispose method is being called in which case these threads will exit. This implementation allows us to centralize the way we execute the processing of work items or execute commands on the work items. As an example, I created a
Logging project where I assign work items to the
Logger to be logged. I’ll cover this in the next section.
As we all know (hopefully), logging is an essential part of writing services and code in general. What I needed was a Logger that can write to a flat file without affecting my business processing too much and that also don’t add too much overhead on the business process’ execution time, i.e., I needed something that was lightweight and super-fast. I also wanted the ability to “plugin” as many different logging adapters as possible. So, I decided to write a little Logging Framework, which allows me to write multiple “adapters” or Command Types for and that writes the logs on a different thread so that it doesn’t affect my Business Process’ execution times. By using the Thread Queue that I explained in the previous section, this became quite a simple job to do. All I needed to do is write the
Logger class passing in the Thread Queue that I wanted to log to and have it sort out the threading, file access and all those other issues that we usually have when we have multiple threads trying to write to the same file. In the case of this article, this became quite a nice little sample application to show how you could use the Thread Queue’s ability to execute Command types.
File Log Command
This command class implements the
ICommand interface which allows us to instantiate the Thread Queue that we use in the
Logger class. It is a simple implementation of a class that is specifically written to write to a file on disk. It handles the file access and also zips the file after the Buffer Size limit as seen in the Logging Settings class is reached. I used SharpZibLib library to do the zipping for me and if you look at the tests, you’ll see that I zip the files after it's logged 10000 lines to the file.
Logger class is the one that will be accessed to assign work to the Queue in most cases. It has a simple
Log method that takes a parameter that implements the
ILogItem. All the log method does is it adds the
ILogItem to the queue by calling the
Enqueue method where a thread contained in the
ThreadQueue will pick it up and then process it by calling the
Execute method on the
I have two utility classes in the project, one for
DateTime formatting and one for zipping the files. I’m not going to explain these seeing that I think they’re quite simple and straightforward.
I’ll keep this at a high level so you can do your own metrics, but if you look at the tests that I have, you’ll see that I have two for load tests, which of course is by no means a good performance test criteria.
Test Case 1 - LoadTestLog
Number of Lines to File: 1,000,000
Zip Buffer Size: 10,000
Number of Line per Second: 100,000+ lines
Number of Line per Milli-Second: 85+ lines
Number of Threads: 1
Test Case 2 - MultiThreadedLoadTestLog
Number of Lines to File per Thread: 100,000
Zip Buffer Size: 10,000
Number of Line per Second: 85,000+ lines
Number of Line per Milli-Second: 85+ lines
Number of Threads: 5
Test PC Criteria
CPU: Intel Core i5 760
If you look at the metrics shown above and you look at the ease with which I implemented this little Logging Framework by using the Thread Queue and Memory Queue as shown in this article. I hope you agree that it can make life a whole lot simpler and that it’s a simple way to get asynchronous processing going without having to implement a very heavy framework and thereby multiplying the number of points of failure in your code.
I hope you enjoyed this article as much as I enjoyed writing it. I’ll be writing some more covering some other concepts soon enough and if you want you can follow me on my site at www.yakiloo.com. Aside from the source code that I explained in this article, you can also keep up to date with some code that I’ll be writing for other articles where I’ll be drilling down into some concepts which include Domain Driven Design (DDD), Test Driven Development (TDD) and REST to name a few. I like playing with concepts and ideas and would love your feedback when it comes to proofing some of those ideas. As part of this, you should also be able to find some of the other implementations of the Memory Queue and Thread Queue and the way I use them in my other articles on my site. If you would like me to add more explanations or more examples of how to implement your own thing by using these classes, you’re welcome to email me or leave a comment and I’ll get back to you soon enough. If you liked this article, please don’t forget to vote for me.