Click here to Skip to main content
Click here to Skip to main content

Base Library for Multi-threaded Windows Services

, 2 Jun 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
This is a base library for multi-threaded Windows Services. It manages starting and stopping services, and defines the base structure.

Introduction

This is a base library for multi-threaded Windows Services. It manages starting and stopping services and defines a base structure.

Library Description

Interfaces

Interfaces.png

There are three interfaces in the library:

  • IConfigurable defines the interface for the classes which can be configured.
  • IRunnable defines the interface for classes that have a determined lifetime for execution.
  • IService defines the interface for service classes.

ServiceManager Class

ServiceManager.png

This class is the main class in the library, and manages the starting and stopping of services configured in the system. This class implements the Singleton Design Pattern.

ServiceSkeleton Class

ServiceSkeleton.png

This is the base of all service classes. All service classes must implement this class.

How it Works

The entry point is the ServiceManager class.

The first step is defining the service classes to the ServiceManager.

ServiceManager.SingleInstance.Services = new List<IService>
{
    new WeatherService(),
    new CurrencyService(),
    new BackupService(),
    new StatusChecker()
};

The second step is signaling ServiceManager to begin the configuration process.

ServiceManager.SingleInstance.Configure();

The third step is signaling ServiceManager to start the configured services.

ServiceManager.SingleInstance.Start();

Designing a Service Class

All service classes must implement the ServiceSkeleton class.

public class WeatherService : ServiceSkeleton

After this, three abstract methods must be implemented too.

  • SleepTime
  • ExecuteInternal
  • ConfigureInternal

When execution time comes, the skeleton class calls the ExecuteInternal method. After work is done, the skeleton class sleeps for SleepTime seconds.

For example, a service which gets currency from a website:

using System;
using System.Net;
using ServiceManagerBase;

namespace MyCompany.MyProject.Services
{
    public class CurrencyService : ServiceSkeleton
    {
        private string _currencyUrl;

        #region Overrides of ServiceSkeleton

        public override int SleepTime()
        {
            return 60*5; // sleep for 5 minutes
        }

        public override void ExecuteInternal()
        {
            try
            {
                Log.Debug(String.Format("Connecting to web site: {0}", _currencyUrl));

                var request = WebRequest.Create(new Uri(_currencyUrl));
                request.GetResponse();

                Log.Debug("Connected.");
            }
            catch (Exception exception)
            {
                Log.Error("Connection failed.", exception);
                return;
            }

            // TODO: Rest of the code goes here...
        }

        public override void ConfigureInternal()
        {
            _currencyUrl = "http://www.sloomedia.com/currency/feeds/USD.xml";

            IsConfigured = true;
        }

        #endregion
    }
}

How to Define a Schedule?

This library is not designed for a schedule operation. But you can schedule jobs. For example:

public override void ExecuteInternal()
{
    //
    // If time is not 02:00:00, exit
    //

    Log.Debug("Checking if time is 02:00:00");

    if (!(DateTime.Now.Hour == 2 && DateTime.Now.Minute == 0 
                                 && DateTime.Now.Second == 0))
    {
        Log.Debug("Time is not 02:00:00. Exiting.");
        return;
    }

    Log.Debug("Time 02:00:00. Starting backup job...");

    // TODO: Do backup job...
}

Stopping the Service: What to do in Service Classes?

When a service is being stopped, the ServiceManager flags the IsStopRequested boolean field to true. In your code, for example, if you are looping a huge array, you have to control this flag so that the service can stop successfully without error.

Let's say that we have a collection.

var users = GetQueue();

In your code, you loop in this collection. Meanwhile, the service is being stopped. You may choose two of these methods:

In the first method, you handle exiting from the loop process.

foreach (var user in users)
{
    if (ServiceManager.SingleInstance.IsStopRequested)
        break;

    Log.Debug(String.Format("Analysing user ID: {0}", user.UserID));

    // TODO: Analyse...
}

In the second method, if you're using complex algorithms and if it'll be hard to handle, you can throw an exception.

foreach (var user in users)
{
    if (ServiceManager.SingleInstance.IsStopRequested)
        throw new StopRequestedException();

    Log.Debug(String.Format("Analysing user ID: {0}", user.UserID));

    // TODO: Analyse...
}

This exception will be caught by the ServiceSkeleton without any problems.

How to Change the Name of the Log File?

By overriding the GetLoggerName method, you can change the name of the log file.

protected override string GetLoggerName()
{
    return "Custom logger name";
}

Base Loop

The base loop in the ServiceSkeleton class is the main loop for every service. It signals the service to execute its own process, manages sleeping, and handles the exceptions.

public void Execute()
{
    Log.Debug(String.Format("Service \"{0}\" entered the execute loop.", this));

    while (!ServiceManager.SingleInstance.IsStopRequested)
    {
        try
        {
            ExecuteInternal();
        }
        catch (ThreadAbortException) {}
        catch (StopRequestedException) {}
        catch (Exception ex)
        {
            Log.Error("Error on internal executing process.", ex);
        }

        Log.Debug(String.Format("Sleeping for {0:n0} second(s).", SleepTime()));

        for (var i = 0; i < SleepTime(); i++)
        {
            Thread.Sleep(1000);

            if (ServiceManager.SingleInstance.IsStopRequested)
                break;
        }
    }

    Log.Debug(String.Format("Service \"{0}\" exited from the execute loop.", this));
}

History

This is the first published version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Efe Erdogru
Software Developer (Senior)
Turkey Turkey
No Biography provided
Follow on   Google+

Comments and Discussions

 
GeneralMy vote is definitely 5 too!!! Pinmemberevrenyagmur11-Feb-11 2:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 2 Jun 2010
Article Copyright 2010 by Efe Erdogru
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid