C# ThreadQueue example.

4. October 2011 19:32

 

This is an example on how to create a thread queue. It is part of a series of posts the full list is avilable here. You may need to look at the previous examples to understand what is happening in the other classes. In this example we will basic a basic class that can be reused to process a queue of items in a background thread.

 

The first thing I should probably point out is that the .net framework does not have an IRunnable interface like java does. I am not sure why this is or at least I have never found something like it. So we will need to create an IRunnable interface this is so that any sort of object passed to the class can be runnable by the queue processor. As any object passed will be required to be implement the same Run function. Code is below for the interface.

 

 

public interface IRunnable
{
	void Run();
}

 

 

Next we are going to create a class which is using the ThreadSimple so that it can run as a background task. We are also going to add a number of functions.

 

  • Add - To add an item to the queue
  • Run - To process the queue and execute the IRunnable interface function.
  • Error event. Fired if when an item throws and exception.
  • Complete event. First then an item finished executing.
  • Count property so the outside world can see how many items are queued.
  • Maximum length property so we can throw an error if too many items are added.

 

We will also add the correct locks to prevent the background thread and the normal application thread from trying to access and modifiy any object's at the same times. This typically caused an issues and headaches in multithreaded programming. The purpose of encapsulating these into the class from the outside world is to limit the scope of locks so that some of the more nasty dfficult bugs occur. If you don't have access to a variable you can't lock it!.

 

First lets keep it simple. We only need the functions Add and Run to demonstrate that this works. We will also start our background thread from the constructor for the queue which is in the ThreadSimple abstract class. It probably also worth pointing out that with lock's we only want to hold the lock for a short period of time. 

 

 

public class ThreadQueue : ThreadSimple
{
	private Queue<IRunnable> Q = new Queue<IRunnable>();

	public ThreadQueue()
	{
		Start();
	}

	public void Add(IRunnable obj)
	{
		lock (Q)
		{
			Q.Enqueue(obj);
		}
	}

	protected override void Run(object obj)
	{
		while (true)
		{
			try
			{
				IRunnable item = null;

				lock (Q)
				{
					if (Q.Count > 0)
						item = Q.Dequeue();
				}
				if (item == null)
				{
					Thread.Sleep(100);
					continue;	//Restart the loop
				}

				item.Run();
			}
			catch (Exception ex)
			{
				throw (ex);
			}
		}
	}
}

 

 

The above example is quite simple. Did you notice the problem though? The sleep should not exist. There is a better way for getting a background thread the sleep until something is added to the queue. We can do this using the class called Monitor. The idea behind this is to send a pulse to anything that is "sleeping" on the object to wake it up. The other thread will then wakeup and start processing the items.

The technical problem with this is normally getting to sleep. The idea is you lock the object then check the state. If the state is such that you need to sleep since there is no work todo then you sleep until somebody changed the state and notifies you. The Monitor.Wait is a special function that will allow you to sleep and unlock the object so that something else can aquire a lock. The lock is then reaquired when the thread wakes.

 

Our code will turn into something like this after the changes.

 

 

public void Add(IRunnable obj)
{
	lock (Q)
	{
		Q.Enqueue(obj);
		Monitor.Pulse(Q);
	}
}

protected override void Run(object obj)
{
	while (true)
	{
		try
		{
			IRunnable item = null;

			lock (Q)
			{
				if (Q.Count == 0)
					Monitor.Wait(Q);

				if (Q.Count > 0)
					item = Q.Dequeue();
			}
			if (item == null)
				continue;	//Restart the loop

			item.Run();
		}
		catch (Exception ex)
		{
			throw (ex);
		}
	}
}

 

I am going to leave the the exercise up the the end reader on implementing the other minor functions for queue length / maximum and events for error's and the when an item is completed. It is also possible to notify from the IRunnable object passed when it has finished executing.

 

What you may not have noticed is that there is an issue about how to destory the class. Since the thread in the background is still running. This really comes down to a design decision depending on your applications. It may be effective to never exist. However let's consider it is required. Another choice may be to stop adding things to the thread wait until the queue is empty then call the Stop function to kill the background thread.

Another possible option that I have used in the past is the make the ThreadQueue support the Dispose method and also inheirt the IDisposeable interface (the standard way todo it in .net). You could then have this function call the stop or an abort (see Thread.Abort) to kill the background thread. Raise an error event on all remaining items on the queue.

 

In the next post I will be focusing more on making this more generic and to support a forceful cancel method.

E-mail Kick it! DZone it! del.icio.us Permalink


Comments (1) -

8/5/2012 10:53:02 PM #

Hi,
Thank you for the incomplete post.
It would have been nice to actually post the entire application...

-Edward

Edward Meshuris United States |

Pingbacks and trackbacks (1)+