A Fistful of WaitHandles - Part One

by Scosby Tuesday, February 21, 2012

Introduction

This is the first post in the series. It talks about the scenario that introduces the new approach and behavior of the Job class in that approach. The second post talks about the technical implementation of the Job class and how to extend the main application’s behavior to suit your needs.

Scenario

If you need to run an operation after a specific amount of time, then it is likely you are familiar with one of the many timers available in the .NET Framework. This is a good approach and is well documented. Furthermore, this approach will continue to be a valuable tool for many developers to use in many different applications.

If you are interested in running the timer’s job on demand, in addition to its interval, then you will need to do a bit more work. Of course, this is still reasonable to do with a Timer but it does provide an opportunity to consider another approach. You will learn about scheduling jobs to the ThreadPool in a way that resembles the familiar timers in the .NET Framework.

Scheduling Jobs to The ThreadPool

The ThreadPool.RegisterWaitForSingleObject method allows you to set up an operation to be run on a schedule or even on demand. This method can seem complex to use at first glance but it can be broken into three parts.

First, you need to start, or register, the job. Next, you provide a special object that helps the ThreadPool know when to run your job. Finally, in order to stop your job, you need to keep a reference to the object returned after you registered the job.

Rather than explain these parts in more detail, I will show you some code and explain it in the context of the previously described scenario. Again, this post is focused on how a job should behave, in order to better understand the problem. The second post will cover all the technical details of the Job class.

Code Samples

The following code samples represent a console application. However, the techniques used can be applied easily in other types of applications, such as a Windows Service or a WPF application. If you can think of any additional uses feel free to comment on the post. Additionally, if you’re up for a challenge, try modifying the program to read job configuration data from the file system at launch.

Program class

        static void Main(string[] args)

        {

            Console.WriteLine("Start executing timer operations:" + Environment.NewLine);

 

            RunJobWithInterval();

 

            RunJobWithNoInterval();

 

            Console.WriteLine("Done executing timer operations." + Environment.NewLine);

 

            Console.Write("Press any key to exit...");

 

            Console.ReadKey(true);

        }

 

        private static void RunJobWithInterval()

        {

            Console.WriteLine("Running Job 1---------------------------");

 

            using (Job job = new Job())

            {

                job.Interval = 500;

 

                job.Start();

 

                Thread.Sleep(1000);

 

                job.Run(); //Possible to run on demand even with an interval

 

                Thread.Sleep(4000);

 

                job.Stop();

            }

        }

 

        private static void RunJobWithNoInterval()

        {

            Console.WriteLine("Running Job 2---------------------------");

 

            using (Job job = new Job())

            {

                job.Start();

 

                job.Run();

 

                Thread.Sleep(200);

 

                job.Stop();

            }

        }

 

This code sample starts with the Main method of the Program class. It calls two other methods, each runs a job either with or without a schedule. It is possible for both jobs to run at the same time. In fact, the jobs are completely independent. This code sample keeps them separated to reduce confusion.

The RunJobWithInterval method first creates a new job, sets the interval, and then starts the job. Next, the application then blocks the thread while the job is running. This gives the job time to write to the console and would not be done in a production application. Next, the method runs the job on demand. Even though the job has a schedule, it is possible to run the job as needed. You can compare and contrast the job’s output for on demand and scheduled operations. Next, the method blocks the thread again to demonstrate how the job will continue to run on its schedule. Finally, the job is stopped.

The RunJobWithNoInterval method is similar but differs in two ways. First, instead of running the job on a schedule, this job is only run on demand. Finally, it only blocks the thread once to allow the job enough time to run before it is stopped.

Thinking in The Problem’s Domain

I have not explained the Job class first, because I feel the semantics of the Job class should not be overlooked. It is important to think of the job in an abstract manner. In fact, this approach is very useful for thinking through other problems. It would have been easy to show the functionality without the Job class. However, in that case you would not get to see the benefits of encapsulation with the Job class. In other words, it should be clear how a job is supposed to behave right now. After all, if you don’t know how something is supposed to behave it becomes much more difficult to write effective code.

Tags: ,

IT | Programming

blog comments powered by Disqus