Click here to Skip to main content
Click here to Skip to main content
Go to top

ThreadManager

, 25 Sep 2005
Rate this:
Please Sign up or sign in to vote.
An easy way to use threading in your applications.

Sample image

Threading – Step by step

.NET is a powerful tool, that can be used by applications that demand high scalability. Without using the Threading namespace, I doubt we can unleash the power of the .NET platform. Depending on the application you write, in most cases ThreadPool should be enough to perform small task asynchronously (in a separated thread). ThreadPool provides no access to the underlying threads, so we have no control over the thread and there’s no way to stop a thread once it has been started. If there’s a need to gain contol over the thread (suspending, aborting) then we have to use Thread instead. But for complex application even the Thread class may need a bit of extra features.

My goal with this article is to:

  • present the basic code from I have developed a complete class to support threading
  • this article also demonstrates:
    • how to use the System.Threading classes
    • gives a basics about working with events and delegates
    • and finally some basics about sybchronization will also be presented

So let’s go...

Threading basics

As I mentioned before the code described below was also used as basic for creating a class library, that allow an easy use of threading. If apply each part of this article plus if you ideas and a lot of work then the you can easily create a similar ThreadManager class as my own.

In .NET it’s really easy to use Threading. First create a class for the Thread with a public Worker function.

public class MyThread {
	public void Worker() {
	}
}
			

Let’s make a simple example: create a thread that will count down from an initial value to zero, then finish. The first thing 1 need to do is to pass the initial counter value to the class. I decided to use the constructor for this [using properties would be also fine, but then we would need also synchronization – and that will be presented later in this „lesson”].

public class  MyThread {
	private int _cnt;
	public MyThread(int initialCounterValue) {
		_cnt = initialCounterValue;
	}
	public void  Worker() {
<FONT color=green>		// do the work
</FONT>		while (_cnt >

Now our thread class is finished. All we need to do is to make the thread work:

public static void Test() {
<FONT color=green>	// create our thread class</FONT>	    
	MyThread mt = new MyThread(100);
<FONT color=green>	// create the threadstart and thread objects 
</FONT>	Threading.ThreadStart ts = new Threading.ThreadStart(mt.Worker()); 
	Threading.Thread t = new Threading.Thread(ts);
<FONT color=green>	// start the thread</FONT>	   
	t.Start(); 
<FONT color=green>	 
	// join is used only in the test function
	// the main thread (console appl.) will wait, until our thread finishes
</FONT>	t.Join();
}

If you call the Start() function from your code, the thread will be started. In a console application you would normally need a call to the Console.ReadLine() function to prevent the main thread (the application) from exitting [if the main application exits, then all it’s threads will be also aborted (if they are backgound threads)]. Here we do not need this call to the Console.ReadLine(). Why? Because we of the line t.Join(). This line makes the main thread (from which the t.Join() was called) to wait until the thread t finishes.

Events and delegates

We’ve created a working thread in the previous chapter. Now we need to generate an event when the thread finishes. We have to create a delegate first. This allows us to pass a function as a parameter. Delegates are type-safe, so the function we will pass as a delegate has to have the same signature as the delegate declaration. Add this line after the declaration of the member variable _cnt.

public delegate void  HandlerForFinish();

Delegate declaration can also have arguments, but we have no need for this at the moment. If you need an example how to use arguments with delegates, please examine the ThreadHandler v1.0 source code.

Now we have to declare the event. Put this line after the delegate declaration.

public event HandlerForFinish OnFinish;

And finally after the thread finishes, our event has to be invoked. This is done by adding the next line right after the // finish comment.

if (OnFinish != null) OnFinish();

As you see, it’s very simple. We first check, if an event is assigned and if sou, we simply invoke them (just like calling any other function in C#).

Now we have to modify the Test() function a little bit, to see the event working. We’ve created a delegate function, then we assigned that function to the OnFinish event.

So the „final” code is:

public class  MyThread {
	private int _cnt;
	public delegate void HandlerForFinish();
	public event HandlerForFinish OnFinish;
	public MyThread(int initialCounterValue) {
		_cnt = initialCounterValue;
	}
	public void Worker() {
<FONT color=green>		// do the work</FONT>		   
		while (_cnt > 0) { 
			System.Console.WriteLine("Counter = " + _cnt.ToString());
			_cnt--;
			System.Threading.Thread.Sleep(500);
		}
<FONT color=green>		// finish</FONT>		 
		if (OnFinish ! = null) OnFinish();
	}
}
public void  Test() {
<FONT color=green>	// create our thread class</FONT>	    
	MyThread mt = new MyThread(100);
<FONT color=green>	// assign a function to the event 
</FONT>	mt.OnFinish + = new HandlerForFinish(mt_OnFinish);
<FONT color=green>	// create the threadstart and thread objects</FONT>	      
	Threading.ThreadStart ts = new  Threading.ThreadStart(mt.Worker());
	Threading.Thread t = new Threading.Thread(ts);
<FONT color=green>	// start the thread</FONT>	   
	t.Start();
	<FONT color=green>	// join is used only in the test function
	// the main thread (console appl.) will wait, until our thread finishes</FONT>	           
	t.Join();
}
private void  mt_OnFinish() {
	<FONT color=green>// this is the time, we can remove the thread from the ThreadManager pool)</FONT>	             
	Console.WriteLine("Thread has finished working...");
}

An alternative ThreadHandler class

In the previous chapter we got very close to the mentioned ThreadHandler class. Before moving to synchronization, we could re-arrange and modify our existing code a little bit and create a mini ThreadHandler.

The ThreadHandler will have a:

  • Constructor
  • Start method
  • Abort method
  • OnJob event
  • OnFinish event
  • OnAbort event
  • OnTerminate
  • OnException event

Also we will create an abstract class which will be used as a schema for the ThreadHandler. Also some public delegate methods will be needed...

The previous example will be transformed to a new structure. The benefits of the new structure:

  • Always the same class will be used when threading is needed.
  • From this point you can focus on creating the code that runs in the thread and you DO NOT HAVE to write extra code to running it in a separate thread.
  • Locks will be added to the ThreadHandler’s published methods – so calling the ThreadHandler functions from different threads will be safe. Also locking mechanism is implemented inside the thread’s working function.

NOTE: The next chapter will give a short description on synchronization.

Delegate methods and EventArgs

For each event we have to create delegates. These will be used also in the public abstract class.

public delegate void HandlerForOnJob(ThreadHandlerEventArgs arg);

The OnJob event is executed after the thread starts. It’ll invoke the worker function and do the calculation in a separated thread.

public delegate void HandlerForOnAbort(ThreadHandlerEventArgs arg);

This event is invoked only, when aborting was demanded.

public delegate void HandlerForOnFinish(ThreadHandlerEventArgs arg);

The Finish event executes when the worker function executed successfully (and completely) ~ so the work was not aborted.

public delegate void HandlerForOnTerminate(ThreadHandlerEventArgs arg);

This event should be raised always when the thread is aborted or it has finished processing. It is for supporting clean-up processes (releasing some resources, removing the ThreadHandler from a collection...

public delegate void HandlerForOnException(ThreadHandlerExceptionArgs arg);

This event is raised, when there is an error inside any of the event-delegates (except OnTerminate).

As you se, we also need 2 special EventArg types:

public class  ThreadHandlerEventArgs : EventArgs {
	private ThreadHandler _threadHandler;
	public ThreadHandler Handler {
		get {
  			return_threadHandler;
		}
	} 
	public ThreadHandlerEventArgs(ThreadHandler threadHandler) {
		_threadHandler = threadHandler;
	}
}
		<P>public class  ThreadHandlerExceptionArgs : EventArgs {
	private ThreadHandler _threadHandler;
	private Exception _exception;
	public ThreadHandler Handler { 
		get {
			return_threadHandler;
	    
		}
	}
	public Exception Ex {
		get {
 			return_exception;
		}
	}
	public ThreadHandlerExceptionArgs(ThreadHandler th, Exception ex) { 
		_threadHandler = th;<	      
		BR >  _exception = ex;
	}
}</P>

 

Abstract class

This class is not necessary. It only helps to make implementation easier.

public abstract class IThreadClass {
	public IThreadClass() {
	}
	<FONT color=green>///
	/// Represents the method that will handle the OnJob event.
	///
</FONT>	public abstract void OnJob(ThreadHandlerEventArgs arg);
	public virtual void OnAbort(ThreadHandlerEventArgs arg) {
	}
	public virtual void OnTerminate(ThreadHandlerEventArgs arg) {
	}
	public virtual void OnFinish(ThreadHandlerEventArgs arg) {
	}
	public virtual void OnException(ThreadHandlerExceptionArgs arg) {
		throw arg.Ex;
	}
}

ThreadHandler alternative

Comments

I will not include the full code from the project. Simply download the sources and check out the ThreadHandler.cs file. Also there is a simple test application (console app.) distributed along with the sources. There is a lot of comments in the ThreadHandler.cs file.

Please read at least the coments in the Worker() functions.

ThreadAbortException

This exception needs a little bit of extra care... When a call is made to Abort to terminate a thread, the system throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught by application code, but is rethrown at the end of the catch block unless ResetAbort is called. ResetAbort cancels the request to abort, and prevents the ThreadAbortException from terminating the thread.

Synchronization basics

The .NET Framework has some classes for synchronisation. The most commonly used synchronisation „tool” is the mutual-exclusive lock (lock statement).

NOTE: locking is only allowed on objects.

Also it’s important avoid the use of the boxing conversion. (Boxing is the conversion of a value type to the type object. Boxing will create a new object from the value type. (For C++ coders: Boxing WILL NOT return the pointer to the original value. It will create a new object and return a reference to the newly created object).

The most usefull function of the lock statement is in it’s simplicity. It has also an special „advantage”: if your code inside a lock-statement throws an error, the exclusive lock is released immediately after leaving the block of the lock-statement.

How lock works: when an exclusive lock is acquired, all other exclusive lock requests (threads from where the lock is requested) are delayed (blocked), until the first exclusive lock is released...

NOTE: You can change the lock statement and use System.Threading.Monitor.Enter(...) and System.Threading.Monitor.Exit(...) for acquiring and releasing the exclusive lock.

About ThreadHandler v2.0

Beta version already exists... At the end of October testing should finish, also documentation should be complete 'till then.

DESCRIPTION

What is ThreadHandler? ThreadHandler is a class allowing you to use threading without additionally coding. To make you more happier, ThreadHandler comes with a ThreadManager class, which is actually an implementation of an advanced thread-pool for the ThreadHandler class.

SPECIAL FEATURES

Any function which is implemented by using ThreadHandler gains the following abilities:

  • Stopping the working process in a given interval (you can define intervals inside the processing code where the processing may be stopped)
  • Pausing the work process at a defined interval (just like stopping – you define when to allow and when to disable pausing)
  • AfterPausing and BeforeResume an additional event is raised. This allows you to release resources after the thread has stopped processing and to re-allocate resources before continuing the work.
  • Functions like WaitUntilPaused and WaitUntilTerminated will block the calling thread (probably the main thread) and invoke a function after the thread has stopped/paused.

OTHER FEATURES

  • A threading class with all the important control functions : Start, Stop, AbortStopping, Pause, PauseImmediately, Abort
  • Easy to use, event driven class : OnStart, OnJob, OnAbort, OnException, OnFinish, OnTerminate
  • AfterSuspend and BeforeResume event!
  • Support stopping and special pausing at any point (you can allow and deny special pausing and/or stopping for any part of your code, by simlpy calling a ThreadHandler function)
  • You’ve got over 20 states, that are used to describe the state of the thread. [There are 20 state constants (enum), that are treated as a bit-field.]
  • 2 function, that will block the caller thread, until ThreadHandler’s worker pauses or finishes (WaitUntilPaused, WaitUntilTerminated )
  • Full access to the System.Threading.Thread object inside ThreadHandler.
  • No need to create a separated (so called) thread-class with a worker function.

STATES, EVENTS, CONTROL FUNCTIONS AND PROPERTIES

Here is a list of the most control functions, properties of ThreadHandler.

States: Invalid, Unstarted, Exception, Running, PausedImmediately, PausingRequested, Pausing, Paused, WaitForResuming, Resuming, Resumed, StoppingRequested, Stopping, Stopped, Aborting, Aborted, Finishing, Finished, Terminating, Terminated.

Events: OnStart, OnJob, AfterSuspend, BeforeResume, OnAbort, OnException, OnFinish, OnTerminate

Control functions/properties: State, Start, Stop, AbortStopping, DisableAbortStopping, Pause, Resume, PauseImmediately, Abort, AllowPausing, AllowStopping, WaitUntilPaused, WaitUntilTerminated.

THE PREVIOUS VERSION (V1.0)

The „previous version” of this component is used in a working environment. It required a FSM (finite state machine) to achieve the same functionality.

In the 2nd version I got rid of this constraint. Also added some extra functions to make the class more better. There are functions that will make the caller thread to wait until the ThreadHandler achieves Paused, PausedImmediately or Terminated state. Also the number of states were increased and synchronization bugs were fixed.

Disclaimer

THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

balazs_hideghety
Web Developer
Slovakia Slovakia
Since 1999 I work in IT. Worked 2-3 yrs with Borland Builder C++. Since .NET appeared, I program in C#, ASP.NET.
 
You may know the current technologies, but still there's a lot of experience to gain. IT's evolving all the time.
 
From 2006 I'm a MCP. Now I'm focusing on technologies like: NHibernate, NSpring...

Comments and Discussions

 
QuestionHow to pause and Resume? PinmemberMichael Kämpf9-Jul-06 20:22 
AnswerRe: How to pause and Resume? Pinmemberbalazs_hideghety26-Apr-07 22:30 
GeneralArticle updated... Pinmemberbalazs_hideghety26-Sep-05 2:37 
GeneralAuthor's comment Pinmemberbalazs_hideghety10-Aug-05 21:51 
GeneralRe: Author's comment PinmemberThe_Mega_ZZTer20-Sep-05 1:26 

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.140921.1 | Last Updated 26 Sep 2005
Article Copyright 2005 by balazs_hideghety
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid