Introduction
This is a jump start and example implementation for those who need to implement a C# Windows Service where the following requirements apply:
- The incorporation of one or more independent worker processes within the Service, each driven by an underlying instance of
System.Timers.Timer
- Start, Stop, Restart, Pause and Resume/Continue calls from the Service Control Manager (Power Events are not included)
- A basic Apache log4net logging implementation that can be further built upon
- Built-in "plumbing" to handle Service state transitions, timer events, disposal of timers, and thread-safe communication between the Service component class and its workers.
Background
You should be aware that there is some debate as to whether implementing Services with System.Timers.Timer
is necessarily a good idea. I have found it to be a reliable method of implementing Services, but there is some dissent amongst respected professionals. For example, see:
http://weblogs.asp.net/jongalloway//428303
A quick word on logging and debugging
Once you've decided that a timer-based Service is what you need, next accept the need for logging. If you want to build services, you will need to dedicate 5% (or more) of your coding efforts to logging. This can feel like a pain if you're coming at it from a desktop apps background, or from any other environment where logging is a secondary consideration, but if you are serious about building Services for production use, I assume the rationale as to the need for extensive logging requires no further explanation here.
Apache log4net is an excellent open-source logging library and I recommend you use it. There are alternatives, but this jumpstart and the code that accompanies it assumes that you are happy with this choice of logging library.
If you have no experience of debugging Windows Services, you should at the very least read both of these articles:
http://msdn.microsoft.com/en-us/library/vstudio/7a50syb3(v=vs.100).aspx
http://msdn.microsoft.com/en-us/library/vstudio/sd8zc8ha%28v=vs.100%29.aspx
Debugging Windows Service projects can be a slightly awkward experience relative to, say, debugging a Console application. A suggested approach is that if you have lots of code that you need to debug and test, keep it in a Windows Library project. Test and debug your code using Visual Studio's unit test features, a Console Application or some other suitable project host type. After you have completed all (or at least, most) of your testing, you can then move that hardened, good-quality code across to a Windows Services project for the final run in. All other things being equal, you want to keep the amount of debugging and testing you have to do against a Windows Service to a minimum, but not to the detriment of code quality.
Keep in mind that Services must be Installed before they are debugged. Installing successfully with installutil
can be a little troublesome for the novice. If you are new to this, do a little research before getting stucking in. It'll save you frustration later.
Jump start
Implementing concrete (derived) classes using the jump start code
The jump start code, encapsulated in the library ServiceTimerLib, consists of two key classes. The first of these is TimerServiceBase
, which inherits from System.ServiceProcess.ServiceBase
and the second is the disposable TimerWorker
class.
You should think of both of these classes as abstract
classes that you inherit from to construct the concrete classes of your Windows Service.
Note: In fact, the TimerServiceBase
class is not implemented as an abstract class, because doing so "breaks" the Visual Studio Service Designer Component. Conceptually, however, both classes are abstract - the intention is that you should derive from them by way of inheritance to construct the concrete classes of your Service.
When a vanilla Windows Service project is created in Visual Studio, the automatically-generated Service component class inherits from System.ServiceProcess.ServiceBase
. When using the jump start code, you change this so that the component inherits from TimerServiceBase
instead. The required chain-of-inheritance remains unbroken because, as just mentioned, TimerServiceBase
itself inherits from ServiceBase
.
Diving straight in to the shallow end, let's show you a concrete implementation of a Service component class:
public partial class PrimeCalcService : TimerServiceBase
{
public PrimeCalcService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
DefaultLog();
LargePrimeWorker largeWorker = new LargePrimeWorker();
SmallPrimeWorker smallWorker = new SmallPrimeWorker();
RegisterWorker(largeWorker);
RegisterWorker(smallWorker);
}
}
What you see in the block above is literally all of the code that is required to implement a functional Service component class using the jump start. Quite a lot more goes on, of course, but most of it is abstracted away in TimerServiceBase
.
A few things happen in the OnStart
method:
- The inherited
DefaultLog
method is called. This does a little work to set up log4net logging. - An instance of
LargePrimeWorker
is constructed. LargePrimeWorker
, which inherits from TimerWorker
, is a worker class that encapsulates, inter alia, a System.Timers.Timer
. - An instance of
SmallPrimeWorker
is constructed. Just like LargePrimeWorker
, it inherits from TimerWorker
and is a worker class with a timer. - The inherited
RegisterWorker
method is called twice - once for each of the workers that have been instantiated.
Before we move on to the concrete implementation of a worker class, let's take a quick detour and look behind the scenes at the private implementation of the RegisterWorker
method used above:
private void _registerWorker(TimerWorker worker)
{
worker.getServiceStateHandler = getServiceState;
if (_log != null)
{
worker.SetLog(LogManager.GetLogger(worker.GetType()));
}
if (_workers == null)
_workers = new ArrayList();
_workers.Add(new WorkerCollectionItem(worker));
There are two things of note that happen here:
Firstly, this line of code...
worker.getServiceStateHandler = getServiceState;
provides each worker with a handle to a delegate function that allows the worker to query the state of the Service. This gives the worker the ability to poll for a change in the state of the Service, for example when Service is changing from a Running state to a Paused state.
Secondly, each worker is added to the _workers
collection, allowing the service component class to subsequently manage signaling for each worker and to manage the disposal of each worker when the Service terminates.
We can look at this in greater detail later. For now, let's move on to an example of a derived worker class.
Here is the class definition and constructor for LargePrimeWorker
:
internal class LargePrimeWorker : TimerWorker
{
private Random r = new Random();
private System.IO.StreamWriter theFile;
internal LargePrimeWorker()
: base (delayOnStart: 30000, timerInterval: 10000, workOnElapseCount: 6)
{
}
When inheriting from TimerWorker
to construct your worker, you must use the base
directive to access one of these two constructors, as there is no useful parameter-less constructor in the base class. In the example above, the second of them is used.
protected TimerWorker(double timerInterval, uint workOnElapseCount)
protected TimerWorker(double delayOnStart, double timerInterval, uint workOnElapseCount)
The only difference between the two constructors is that the former causes work to start immediately, whereas the latter implements an initial delay before work begins.
Before proceeding further, let me explain how the underlying Timer
operates in line with the arguments you pass in to the base constructor. As you would expect, the Timer
elapses at regular intervals consistent with the timerInterval
value specified. However, the elapse does not cause work every time it occurs. Instead, a private counter is incremented on each elapse. Only when the counter reaches a value equal to the workOnElapseCount
value is the counter reset to zero and work carried out. Nevertheless, on every elapse, the worker class makes a query to its service component class to determine if there has been a change in the state of Service.
To help explain this more clearly, let's look at how the underlying Timer
will operate in the LargePrimeWorker
example shown above.
The values passed in to the constructor are:
delayOnStart: 30000, timerInterval: 10000, workOnElapseCount: 6
In this case, the following will happen:
- When the worker is constructed, the timer will sit idle for approximately 30 seconds (delayOnStart = 30000ms)
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the first time. The private counter will equal 1. On elapse, the worker will query the service for any change in its state.
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the second time. The private counter will equal 2. On elapse, the worker will query the service for any change in its state.
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the third time. The private counter will equal 3. On elapse, the worker will query the service for any change in its state.
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the fourth time. The private counter will equal 4. On elapse, the worker will query the service for any change in its state.
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the fifth time. The private counter will equal 5. On elapse, the worker will query the service for any change in its state.
- After a further period of approximately 10 seconds (timerInterval = 10000ms) the timer will elapse for the sixth time. The private counter will equal 6, which is equal to the value of workOnElapseCount. On elapse, the worker will query the service for any change in its state, the private counter will reset to 0, and work will be carried out.
- All of the above happens again (without the initial delay)
You can see from this example that work will be carried out approximately every 60 seconds and that the Service state will be queried approximately every 10 seconds. The jump start code encourages you to construct timers that query the Service state often. The advantage of doing so is that, provided the work you execute also completes within a reasonable period of time, your workers will remain responsive to changes in the state of their Service. This is important, because the Service Control Manager (SCM) requires that Services respond to its requests in a timely fashion. Do not underestimate the importance of getting this aspect of your Service behaviour right.
Note: You can configure workOnElapseCount
with a value of 1
, in which case work will be carried out on every elapse of the Timer. A key to constructing workers that are responsive to service state transitions is to ensure that the work code executed by your worker completes within a reasonable period of time; if it typically runs for more than 50-60 seconds, I recommend that you try to find a way to break it down in to smaller units. If your Service needs to get through a lot of work in a relatively short period of time, you should be looking to build workers that execute fast-running work methods more often, rather than workers that run code with lengthy execution times less often. You cannot set workOnElapseCount
to a value of 0
- doing so throws an exception.
Let's look at some more code from the example implementation of the concrete worker class:
protected override void StartWork(TimerWorkerInfo info)
{
OpenFile();
this.Log.InfoFormat("File '{0}' opened",
(theFile.BaseStream as System.IO.FileStream).Name);
}
protected override void Work(TimerWorkerInfo info)
{
int N;
long prime;
Prime.GetNthRandomPrimeLarge(r, out N, out prime);
theFile.WriteLine(string.Format("Calculated prime number with ordinal {0}", N.ToString()));
theFile.WriteLine(string.Format("The prime is {0}", prime.ToString()));
theFile.WriteLine(info.ToString());
theFile.Flush();
}
- Override the
StartWork
method to carry out any initialisation work. This method will execute only once, the first time that the underlying Timer elapses to carry out work. It is a good idea to limit the amount of code you execute in the constructor of your worker. Where possible, place that initialisation code in StartWork
instead. - Override the
Work
method to carry out your work. - The
info
object of type TimerWorkerInfo
is made available via numerous methods, to provide some simple statistics and information about the underlying Timer. Further examine the source code to see what properties are included.
protected override void OnContinue(TimerWorkerInfo info)
{
base.OnContinue(info);
OpenFile();
}
protected override void OnPause(TimerWorkerInfo info)
{
base.OnPause(info);
CloseFile();
}
protected override void OnStop(TimerWorkerInfo info)
{
base.OnStop(info);
CloseFile();
}
protected override void OnShutdown(TimerWorkerInfo info)
{
base.OnShutdown(info);
CloseFile();
}
The purpose of each of the above overrides ought to be self-explanatory, but I'll be brief and explicit lest there be any doubt:
- Override
OnContinue
with any code that should be executed by your worker when your Service is Resuming having been Paused - Override
OnPause
with any code that should be executed by your worker when your Service is transitioning from Running to Paused - Override
OnStop
with any code that should be executed by your worker when your Service is Stopping - Override
OnShutdown
with any code that should be executed by your worker when your Service is Stopping due to a system shut down
Note: I confess that I have yet to discover any good reason for implementing different code for OnStop
than for OnShutdown
. Either way, your Service is stopping, right? I'm sure that other experts will be able to explain why Windows Services handle these events separately. I've never researched this for myself.
With that, we have covered the main aspects you need to be aware of to create your concrete classes using the jump start code. The above explanatory notes, together with a review of the sample code included in the article attachments, should be enough to get you well on the road to implementing your Service. The sections below offer additional insights into how it all works.
Service and worker co-ordination
In this section, I'll explain how the abstracted jump start code co-ordinates Service state transitions between a Service component class and its workers.
Co-ordinating the transition of a Service state, for example a transition from Running to Stopping, presents us with this scenario:
- When the Service class component needs to stop, we require that its workers stop working, and in all but the most trivial of cases, the workers must be given an opportunity to stop gracefully (typically, each worker will persist some kind of data and dispose of whatever resources it is responsible for)
- The workers will be executing code on a different thread to the Service class component, driven as they are by an underlying
System.Timers.Timer
- We therefore need to take some care to manage this communication between threads properly
- As the jump start code abstracts both the Service class component and each of its workers, we cannot use concrete methods to meet these requirements. Doing so would undermine the benefits of abstracting this logic away into re-useable classes.
To see how this is handled, first take a look at this block from TimerServiceBase
:
private ServiceState _serviceState
= ServiceState.Running;
private object _serviceStateLock
= new object();
internal enum ServiceState
{
Running = 0,
Pausing = 1,
Paused = 2,
Stopping = 3,
ShuttingDown = 4,
Stopped = 5
}
internal delegate ServiceState getServiceStateDelegate();
internal ServiceState getServiceState()
{
lock (_serviceStateLock)
{
return _serviceState;
}
}
The ServiceState
enum is used to describe the various Service states, and the current state is stored in the _serviceState
variable of the same type. As we need to access and manipulate this variable across numerous threads, a reference object _serviceStateLock
is created for thread locking purposes, and any code that needs to access the variable is surrounded by a lock
clause.
Note: Rather than using a lock
clause, we might have been able to use one of the newer Interlocked
methods provided by the Framework. Personally, I prefer lock
because it makes code more readable.
To address the issue of needing to abstract the code that facilitates the worker classes querying the state of the service, we create a delegate with a parameter-less signature and a ServiceState
return type:
internal delegate ServiceState getServiceStateDelegate();
... and implement a function with a matching signature ...
internal ServiceState getServiceState()
... and then, when each worker is registered using the RegisterWorker
method, we pass a handler to this method off to the worker:
worker.getServiceStateHandler = getServiceState;
In the TimerWorker
class, on each elapse of the timer, we use a call to the getServiceStateHandler
handler to query the current state of the Service, and to take whatever action may be necessary:
private void _QueryAndHandleServiceState(TimerWorkerInfo info, out bool doWork, out bool stop)
{
TimerServiceBase.ServiceState state
= getServiceStateHandler();
The final piece of the puzzle is to ensure that each worker has an opportunity to stop gracefully when the Service needs to stop. This requirement is met by associating a ManualResetEvent
with each worker as it is registered. You don't see this code in any of the snippets above, because it's slightly tucked away, but here it is, seeing the light of day:
worker.signalEvent = new ManualResetEvent(false);
You can get a better idea of how these ManualResetEvent
signals are used by taking a look at the OnStop
override of the TimerServiceBase
class. (The OnPause
implementation works in a very similar way as far this concept is concerned)
protected override void OnStop()
{
try
{
if (_log != null)
{
_log.Info(logmessages.ServiceOnStop);
}
_resetSignals();
lock (_serviceStateLock)
{
_serviceState = ServiceState.Stopping;
}
_waitSignals();
lock (_serviceStateLock)
{
_serviceState = ServiceState.Stopped;
}
if (_log != null)
{
_log.Info(logmessages.ServiceStopped);
}
}
finally
{
base.OnStop();
}
}
The above block is not entirely verbose because some code is tucked away in the private methods _resetSignals
and _waitSignals
, but hopefully this is sufficient for you to grasp the concept. Essentially, what happens is as follows:
- A message is (possibly) logged
- The
ManualResetEvent
associated with each worker is Reset
- The Service state is changed to Stopping
- The Service waits until each and every worker has detected the new state of the Service, completed its work, come to a stop, and has
Set
its ManualResetEvent
- The Service changes state to Stopped and (possibly) logs a further message
Disposal
It is important that you understand how the jump start code helps you with disposal of resources and where you need to pick up the slack yourself.
Firstly, observe that:
TimerWorker
explicitly implements IDisposable
and therefore any worker class you derive from it is IDisposable
TimerServiceBase
implements IDisposable
because it is derived from an IDisposable
, and as such, any Service component class you derive from TimerServiceBase
is also IDisposable
The good news is that TimerServiceBase
incorporates code that ensures that the Dispose
method of each worker is called. (The prerequisite is that a call to RegisterWorker
must have been made for the worker, but if you hadn't done that, your worker wouldn't run anyway). So, you don't need to concern yourself with making those calls happen, and disposal of the underlying System.Timers.Timer
is of course taken care of for you.
Note: In the vary rare case that it be of any consequence, and for whatever it may be worth, the disposal code in TimerServiceBase
will call Dispose
for each worker in the reverse order to that in which the workers were registered. That is to say, the worker that was passed to the first call of RegisterWorker
will be disposed of last, and the worker that was passed to the last call of RegisterWorker
will be disposed of first.
The not-so-good news, which should hardly come as any surprise, is that the usual best practise Disposing rules still apply. If, for example, you use a managed IDisposable
resource in your concrete worker class, you must:
- Use the resource with
using
directives so that it is automatically disposed; or - For any resources absent
using
, correctly implement the protected override
dispose pattern, dispose of your resources therein, and ensure that you make a call to base.Dispose(disposing)
as shown in the following block; or - Otherwise ensure that the resource is disposed of appropriately.
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
if (theFile != null)
theFile.Dispose();
}
disposed = true;
base.Dispose(disposing);
}
Logging with log4net
There's a wealth of resources on how to use the library so I have deliberately kept this section brief.
In your Service component class, you can call the method DefaultLog
, which gets your class an ILog
logger using the standard methodology:
_log = LogManager.GetLogger(this.GetType());
You can then access this in your derived class by way of the Log
property.
A little more helpfully, this carries over to your workers, in as much as they each get their own type-specific _log
instantiated automatically provided that you have made the call to DefaultLog
in your service class before you make your calls to RegisterWorker
.
Just as for the service component class, in each of your derived worker classes you can use the Log
property to do your logging. (It may be sensible to check Log != null
in your code, though, to avoid null-ref exceptions).
The example PrimeCalService includes a working log4net xml-based configuration that you can reference as a starting point if you are new to log4net. Just take a look at the app.config
.
I recommend you spend some additional time looking into the log4net documentation, because there are myriad ways to configure and use this library.
Carrying out work with longer execution times
I mentioned previously my belief that breaking your work down into units with short execution times is one of the keys to building Services that behave well.
If you have a need to run code in Work
that is likely to execute over several minutes or longer, you are going to experience issues using the jump start code when your Service is stopped or paused by the SCM. The jump start code assumes that the timer of each worker will elapse regularly enough to ensure that the worker is adequately responsive to changes in the state of its Service; and the underlying timer will not elapse at all between entering and exiting Work
. If this doesn't make sense to you, take a read through and understand the TimerWorker
code, and I'm sure it won't take you long to see the problem.
You do, however, have the option of checking the service state during the execution of your Work
. The base class provides a boolean ServiceStateRequiresStop
property for exactly this purpose. If you are able to check its value often enough as your work executes and you are able to quickly break out of your work should the result be true
(which means the Service state is no longer Running), that might be enough to keep your Service responsive.
Final thoughts
- If you need to build timer-based Services for production use, you will probably want to take a close look at whether or not the jump start code includes adequate exception handling to meet your needs. You are likely to need to do your own additional work on this - the code I am providing has not been extensively tested, and I'm offering it up to the community as a conceptual model rather than anything more. In short, I'm giving you a jump-start here - not buying you a Ferrari.
- There are probably weaknesses in this first edition of the code (I'm aware of several myself but have not yet had an opportunity to address them.) If you have ideas and feedback, please don't hesitate to let me know. I welcome any amount of constructive criticism, and will make the effort (as time allows) to incorporate improvements and update the article accordingly.
- If you are new to Services, a few small tips that may save you some pain: (1) the first time you install with installutil, make sure that you have both added and configured an installer using the VS designer and that your project has been built with that installer in place; and that you run installutil with elevated (Run as Admin) privileges; (2) consider carefully which account to run your Service under. Local System is usually enough if your Service is accessing only local resources, but if your Service code needs to access a remote database, for example, you may need to consider other options; (3) when debugging with Attach to Process, always start Visual Studio with elevated privileges (Run As Admin).
Acknowledgements
Embedded in the Sample service attached to this article is a modified routine for the calculation of Prime numbers. The original routine was sourced here:
http://stackoverflow.com/questions/13001578/i-need-a-slow-c-sharp-function
It is not explicitly credited by the person who posted it. If it belongs to you, please let me know and I will be happy to formally acknowledge you here.
History
This is the initial publication of this article.