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

ActiveRecoverySwitcher

, 3 Nov 2013
Rate this:
Please Sign up or sign in to vote.
How to switch between N number of threads - primary and secondary services. Show a way to implement backup service switch. This could be easily converted to load balancing service.

Introduction

This is a prototype / proof of concept project which shows how resources can be switched intuitively when some are not available. It was designed specifically for switching between N number of Automatic Identification System (AIS) live feeds - TCP Streams, however to easily implement this Demo we would not be using these live feeds instead we will be working with Files which would represent TCP streams.

Background

Please note that a working knowledge of AIS is not required to carry out this demo but if you would like some background information please follow these links.

AIS messages comes as a TCP stream from different AIS providers (http://www.aishub.net/, http://marinetraffic.com/). The AIS message stores information for the ID of the vessel, the speed the destination and lots more, for more info please follow these links:

The Concept

The idea is that if a file exists, then this means that the service is available and vice versa. These services should also have a priority, this could be specified in a data structure. In this demo the files have also been prioritised by naming them in an ordered form from "TextFile0.txt" as the is highest priority to "TextFileN.txt" with lowest priority.

When "TextFile0.txt" is available, we do not need to use its next available service "ThreadFile1.txt" (i.e. the thread with the next priority). But if the "TextFile0.txt" is not available, then check the next and so on till the maximum possible service(File) in the collection is reached.

If currently we are using (the fourth service) "TextFile3.txt", then this means that there are three other services with higher priority which could probably be restored later on while we still use "TextFile3.txt". That's why we need to periodically check if the other services are available = if the other files exists.

ActiveRecoverySwitcher1

Figure 1 - Simulating switching between N number of TCP Streams with Files.

Testing the Software

To test the software just run the application and begin to change the names of the files (check Files folder for project test files). When you change the file "TextFile0.txt", the application listens for the next service(file) "TextFile1.txt" You would notice that after some time there will be a check again if TextFile0.txt exist (if the service is restored).

Note that "App.config" - stores configuration of the number of services = files = threads

There is always one main thread - the main thread of the console application.

Points of Interest - Source Code Explanations

In order to check if pervious file / Thread exists I am using Timers. But because every timer correspond to a thread I have extended the Timer class, and added ID - which is the ID of the thread, as well as some addition fields (these were just for description purpose). public class ServiceTimer : Timer

public class ServiceTimer : Timer
{
    /// <summary>
    /// Service ID == TimerID (every timer is associated with a service)
    /// </summary>
    /// <value>
    /// The unique identifier.
    /// </value>
    public int Id { get; set; }
    /// <summary>
    /// Gets or sets the name of the timer. (debuggingh purposes)
    /// </summary>
    /// <value>
    /// The name.
    /// </value>
    public string Name { get; set; }
    /// <summary>
    /// Gets or sets the description. (debuggingh purposes)
    /// </summary>
    /// <value>
    /// The description.
    /// </value>
    public string Description { get; set; }

Timers are managed by the "ServiceTimers.cs" class. Which contains a dictionary collection of Timers. A Tick method is used to specify a delegate which will process Elapsed event for a service with specific "id".

public class ServiceTimers
{
    public delegate void OnElapsedTime(object sender, ElapsedEventArgs e);
    public OnElapsedTime OnElapsed { get; set; }
    private Dictionary<string, ServiceTimer> timers = new Dictionary<string, ServiceTimer>();
    public void Add(ServiceTimer serviceTimer, OnElapsedTime onElapsed)
    {
        OnElapsed = onElapsed;
        timers.Add(serviceTimer.Name, serviceTimer);
    }
    public void Tick(int id)
    {
        foreach (var t in timers.Values)
        {
            if (t.Id == id)
            {
                t.Elapsed += new ElapsedEventHandler(OnElapsed);
                break;
            }
        }
    }
}

Lets now have a look at the main class: "ActiveSwitcher.cs".

It has a couple of arrays with size equal to the size of the services - files available.

  • bool[] exit = new bool[ServicesCount]; - set to true only before stopping the threads.
  • bool[] works = new bool[ServicesCount]; - set true if the file - the service works correct and false if the file do not exists.
  • object[] lockers = new object[ServicesCount]; - collection of locking object - when we have to lock code in critical section.
  • CheckResource(int resourceID); - this delegate is used because is better the check of the resource to be done outside the class. The idea is that we want to make the ActiveSwitcher work as a black box - just specify the number of services, a way to check them and timer intervals to check.

In the Main() method of the application - in Program.cs, this delegate is assigned to Exist method - this method just checks if the file in the specified path exists.

In the Init() method all of the above arrays are initialized. As it is commented in the code only the main thread is marked works[0] = true;. We need that to start the checking of the services.

private void Init()
{
   for (int i = 0; i < ServicesCount; i++)
   {
       exit[i] = false;
       lockers[i] = new object();
       works[i] = (i == 0);    //only the main thread is marked that it works
    } 
}

StartThreads(): When we start a service we need to have the backup service id specified as well. The implementation is done in that way so when we reach the last possible service(file) we specify that the backup service is the one before. Other logic can be applied here if necessary.

private void StartThreads()
{
    for (int i = 0; i < ServicesCount; i++)
    {
        if (i == ServicesCount - 1)
        {
            new Thread(Work).Start(new CurrentAndNextService(i, i - 1));
        }
        else
        {
            new Thread(Work).Start(new CurrentAndNextService(i, i + 1));
        }
    }
}

InitTimers(): Initializes all the timers which correspond to threads. The main thread timer - with id=0 is set to 100 ms, all the others are set to 2000ms = 2seconds. After the initialization, Tick event is called for all the threads.

private void InitTimers()
{
    for (var i = 0; i < ServicesCount; i++)
    {
        _testServiceTimers.Add(i == 0 ? new ServiceTimer(i, 100) : 
                new ServiceTimer(i, 2000), OnElapsedTime);
        // the main thread is released every 100 ms the other times elapsed on every 2000 ms
    }
    for (var i = 0; i < ServicesCount; i++)
    {
        _testServiceTimers.Tick(i);
    }
}

StopExecutionOfAllLockers(): Set the exit[] value for all the threads to true - this will mean that the thread is ready to exit. In the simulation this is done on the 9997 execution of the main thread. If this is a Windows Service Application we can call that when the user stop event is called.

public void StopExecutionOfAllLockers()
{
    for (var i = 0; i < lockers.Length; i++)
    {
        lock (lockers[i])
        {
            exit[i] = true;
            Monitor.Pulse(lockers[i]);
        }
    }
}

PulseFirstWorkingLocker(): Release the thread which is marked to work, the one which has works[i]=true. The timer which changes the value of works to other threads - with lower priority in order to check if eventually the service is returned - works correctly (check OnElapsedTime event in ActiveSwitcher.cs).

public void PulseFirstWorkingLocker()
{
    for (var i = 0; i < works.Length; i++)
    {
        lock (MainLocker)
        {
            if (works[i])
            {
                lock (lockers[i])
                {
                    Monitor.Pulse(lockers[i]);
                    break;
                }
            }
        }
    }
}

The class CurrentAndNextService as the name suggests contains of the ids of the current and the next service - this is used in the StartThreads() method.

public class CurrentAndNextService
{
    /// <summary>
    /// Gets or sets the current service identifier.
    /// </summary>
    /// <value>
    /// The current service identifier.
    /// </value>
    public int CurrentServiceId { get; set; }
    /// <summary>
    /// Gets or sets the next service identifier.
    /// </summary>
    /// <value>
    /// The next service identifier.
    /// </value>
    public int NextServiceId { get; set; }
    /// <summary>
    /// Initializes a new instance of the <see cref="CurrentAndNextService"/> class.
    /// </summary>
    /// <param name="currentServiceId">The current service identifier.</param>
    /// <param name="nextServiceId">The next service identifier.</param>
    public CurrentAndNextService(int currentServiceId, int nextServiceId)
    {
        CurrentServiceId = currentServiceId;
        NextServiceId = nextServiceId;
    }
}

I am passing the service id in the input parameter of "Work" method. This method is started as a new thread. That's why the id of the service is passed as an object. It seem that this is the only appropriate type when you create a new thread. Inside the Work method there is infinite loop which stops only when the thread is marked as exit[]= true; If not then it just prints to the console the id of the thread.

private void Work(object currentSercice)
{
    var currentAndNext = (CurrentAndNextService)currentSercice;
    //use the locker of the current service
    lock (lockers[currentAndNext.CurrentServiceId])
    {
        while (true)
        {
            Monitor.Wait(lockers[currentAndNext.CurrentServiceId]);
            if (!exit[currentAndNext.CurrentServiceId])
            {
                Console.Write((currentAndNext.CurrentServiceId).ToString(CultureInfo.InvariantCulture));
                //check if the current service is working correctly
                if (Check(currentAndNext.CurrentServiceId))
                {
                    works[currentAndNext.CurrentServiceId] = true;
                }
                else
                {
                    works[currentAndNext.CurrentServiceId] = false;
                    lock (lockers[currentAndNext.NextServiceId])
                    {
                        works[currentAndNext.NextServiceId] = true;
                    }
                }
            }
            else
            {
                //if exit is true then go out of the while loop
                //exit[] becomes true when the service is signaled to stop
                Console.WriteLine("\nlastly called t" + currentAndNext.CurrentServiceId + "\n");

                break;
            }
        }
    }
}

Evaluation of what has been done

Concurrency Visualizer is a part of the Visual Studio package.

For the purpose of the test I will make the application stop after 100 calls.

When the thread started - the first part of the simulation:

When the threads stop - the last part of the simulation:

674753/ActiveRecoverySwitcher3.png

What conclusions we can get from the visualization?

Most of the time is spent for synchronization: 95%. The sleep takes 5%. I guess that DirectX GPU Engine is about the console application. Looking at the visualizations and the percentages I was not able to notice anything strange or unexpected. Here should be noted that the process switching is different when different files exists. There are much more checks if more of the services - files do not exists.

License

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

About the Author

Lyubomir Rumenov Velchev
Software Developer (Senior) Softopia
United Kingdom United Kingdom
No Biography provided
Follow on   Twitter   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSteff_675-Nov-13 8:13 

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 | Mobile
Web03 | 2.8.140721.1 | Last Updated 3 Nov 2013
Article Copyright 2013 by Lyubomir Rumenov Velchev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid