Introduction
To readers: This article was posted last week under a different title. I deleted it later because the code does not really make multithreading easier, it requires you to derive a class from my base class and then override a virtual method. The current version is much simpler. I hope you find it useful, too.
When programming with C/C++, you can create a new thread using the Win32 API CreateThread. This API allows you to pass the address of a function and a pointer as its parameter. The function will be executed in a new thread. But you cannot use just any function as the thread function and you have to cast the input pointer to a structure or a class to access various values you need in the thread function. Or you can access global variables within the thread function to get input values.
With .NET, threading is supposed to be easier. However, the thread function you use in .NET cannot take any input parameter directly. You need to do some work to pass parameters to your worker thread, you can find out how to do it from MSDN.
In this article, I will introduce a C# class, ThreadHelper, that allows you to call almost any method that takes any number of parameters from a new worker thread. For example, you can use it to call a method in a .NET framework class or a third-party DLL from a new thread. The helper class also provides a way for you to get the output value of your function from the new thread.
The ThreadHelper class
The purpose of this class is to hide some boring details of using the .NET thread classes and make it easier for you to create a new worker thread in your application. If you are the kind of developer who always wants to know everything in your application, then this class is probably of little interest to you.
Here are the public methods of the ThreadHelper class.
public ThreadHelper();
public void SetParameter(Object oValue);
publicvoid SetMethod(Delegate pMethod);
public bool StartThread();
public void WaitForThread();
public String GetErrorMessage();
public Object GetOutput();
public void Reset();
Suppose you have a class MyDataClass as follows. The static member function GetData connects to a specified database and executes a query to retrieve data into a DataSet object.
class MyDataClass
{
public static DataSet GetData(String sConnection, String sSQL)
{
};
};
You may want to call this static member function to get data from several different databases simultaneously. Using the ThreadHelper class, here is what you need to do:
- Define a delegate for the
GetData method.
- Create
ThreadHelper objects.
- Call methods
SetParameter, SetMethod, and StartThread.
- To wait for the thread to finish and check error messages or output values, call methods
WaitForThread, GetErrorMessage and GetOutput.
Here is the code for the above example:
public delegate String myDelegate(String sConnection, String sSQL);
...
ThreadHelper objHelper1 = new ThreadHelper();
ThreadHelper objHelper2 = new ThreadHelper();
ThreadHelper objHelper2 = new ThreadHelper();
objHelper1.SetMethod(new myDelegate(MyDataClass.GetData));
objHelper2.SetMethod(new myDelegate(MyDataClass.GetData));
objHelper3.SetMethod(new myDelegate(MyDataClass.GetData));
objHelper1.SetParameter(sConnection1);
objHelper2.SetParameter(sConnection2);
objHelper3.SetParameter(sConnection3);
objHelper1.SetParameter(sStatement1);
objHelper2.SetParameter(sStatement2);
objHelper3.SetParameter(sStatement3);
objHelper1.StartThread();
objHelper2.StartThread();
objHelper3.StartThread();
objHelper1.WaitForThread();
objHelper2.WaitForThread();
objHelper3.WaitForThread();
Object oOutput = ObjHelper1.GetOutput();
...
As you can see, it is easy to call a method from a new worker thread with the help of ThreadHelper and you don't have to know anything about the thread classes in .NET.
Note: If your method has multiple parameters, you need to call SetParameter multiple times and the calls have to be in the same order as the parameters appear in the method signature. The same ThreadHelper object cannot be used simultaneously in different threads. You need to use a separate helper object for each concurrently running thread. If you call the Reset method after the worker thread finishes, then the helper object can be reused to start another thread.
A test application
I have included a C# console application ThreadHelperTest.exe with this article. The application uses ThreadHelper to start two worker threads, each calls the non-static Test method of class MyTest. Here is the corresponding code:
...
public delegate bool TestCall(String sName, int nTest);
...
MyTest objTest = new MyTest();
ThreadHelper objHelper1 = new ThreadHelper();
ThreadHelper objHelper2 = new ThreadHelper();
objHelper1.SetMethod(new TestCall(objTest.Test));
objHelper2.SetMethod(new TestCall(objTest.Test));
objHelper1.SetParameter("Bill");
objHelper2.SetParameter("George");
objHelper1.SetParameter(1);
objHelper2.SetParameter(2);
objHelper1.StartThread();
objHelper2.StartThread();
objHelper1.WaitForThread();
objHelper2.WaitForThread();
String sError1 = objHelper1.GetErrorMessage();
String sError2 = objHelper2.GetErrorMessage();
if(sError1=="")
{
System.Console.WriteLine("Output of test 1: " +
objHelper1.GetOutput().ToString());
}
else System.Console.WriteLine("Error in test 1: " + sError1);
if(sError2=="")
{
System.Console.WriteLine("Output of test 2: " +
objHelper2.GetOutput().ToString());
}
else System.Console.WriteLine("Error in test 2: " + sError2);
The following is the console output from this test application:
Hello, Bill
This is test 1
The thread id is 2
Hello, George
This is test 2
The thread id is 3
Hello, Bill
This is test 1
The thread id is 2
Hello, George
This is test 2
The thread id is 3
Hello, Bill
This is test 1
The thread id is 2
Hello, George
This is test 2
The thread id is 3
Output of test 1: True
Output of test 2: True
Note: The ThreadHelper class is supposed to make your multithreading code easier. It uses DynamicInvoke internally, which may be a little slower than the standard way (late binding vs. early binding). Also, you need to make sure that the method you want to execute in a new thread is thread-safe if it will be used concurrently in multiple threads.
Thank you for reading my articles.
Recent updates
- 11/04/2003
- Changed title and text.
- Simplified code a little bit.