Multi threading- The Killer tool
.NET threading is a powerful tool that can be used by applications that demand high scalability. Without using Threading
namespace, I doubt if we can unleash the real power of .NET platform. All the back end applications like Database servers, Web servers, Queue management systems and the applications like Word, Excel use multi threading.
In .NET, the perfect OO platform threads are also created and accessed as objects.
The main and multiple worker threads model, is used by many applications. In this model a parent thread starts many worker threads based on the service required. All the worker threads do the same work but within a pre-defined context set by the parent. E.g. In a database server application a new worker thread is created whenever a new client is connected. This is an over simplified example and the actual logic is more complex than this.
In this model the worker thread may want to send messages to the main thread to report its progress. The crucial part in this messaging is that the message notifying code should be executed by the Parent thread and not by the worker thread.
This project can be used as a tool to accomplish the above mentioned. This is a Thread pool management system with communication facility enabled. There is a class called ThreadManager
which has the facility for its client to define the number of threads, the method that should be invoked by the worker thread and the method that should be called when notifying the parent thread.
The class implements an interface
that is shown below:
public interface IThreadManager
{
int ThreadCount
{
get;
}
void SetRunMethod(MethodNoParam Run);
void SetMessageReceiveMethod(MethodWithParam Receive);
void Start();
void Stop();
void SendMessageToMain(object Message);
}
- Number of threads to be started is specified in the constructor.
SetRunMethod
expects a delegate which points to the code executed by the worker threads when they are started.
SetMessageReceiveMethod
is used to specify the message receiving code executed by the Main
thread
SendMessageToMain
is invoked by the worker thread when it wants to send messages to main thread.
_ThreadMgr = new ThreadManager(Convert.ToInt32
(ThreadCount.Value.ToString()));
MethodNoParam _RunMethod = new
MethodNoParam(this.ThreadMehod);
MethodWithParam _ReceiveMethod = new
MethodWithParam(this.CallBackMethod);
_ThreadMgr.SetRunMethod(_RunMethod);
_ThreadMgr.SetMessageReceiveMethod(_ReceiveMethod);
_ThreadMgr.Start();
MethodNoParam
and MethodWithParam
are the delegates defined at the global level.
When the worker threads started, they increment the NoOfRecordsUpdated
variable defined in the client till it reaches RecordsToBeProcessed
. Worker threads, after each update, notify the main thread by calling SendMessageToMain
. Any data can be passed on to the Main
thread and the implementation is left to the client. In this case client sends the number of records updated to Main
.
private void ThreadMehod()
{
try
{
while (true)
{
Monitor.Enter(this);
if (RecordsProcessed >= RecordsToBeProcessed)
{
Monitor.Exit(this);
break;
}
else
{
RecordsProcessed +=20;
Monitor.Exit(this);
_ThreadMgr.SendMessageToMain(RecordsProcessed);
Thread.Sleep(500);
}
}
}
catch
{
}
}
The main thread extracts the data passed by the worker threads. In this case it updates the progress bar.
private void CallBackMethod(object State)
{
this.Invoke(_ShowCurrentState,new object[]{State});
}
The thread which executes this code can be confirmed as Main by un-commenting the second line.
The ThreadManager
is using a Queue based mechanism for the message delivery.
For other design patterns such as pipeline model where one thread is dependent on the other, we can use the built in functions such as Wait
,Join
etc. The application defines two delegates which are used by the client. The client which is a Windows form, has methods defined that should be executed by the main and worker threads. The message that is received by the so called Main
thread is passed on to the UI thread using the Invoke
method. All the visual objects that can be created on a Windows form are inherited from the Control
class. The control class exposes Invoke
method which accepts a delegate.
Following shows the DispatchMessage
method.
public void DisptachMessage()
{
while(m_Started)
{
Monitor.Enter(this);
while(m_Messages.Count > 0)
{
m_MessageReceiveMethod(m_Messages.Dequeue());
}
Monitor.Exit(this);
Thread.Sleep(100);
}
}
As the Main
thread in this example is engaged in dispatching the messages all the time, usage of UI thread for this purpose is discouraged.
private void CallBackMethod(object State)
{
this.Invoke(_ShowCurrentState,new object[]{State});
}
The message that is received by the Main
thread, which is Records updated in the example, is passed to UI which updates the progress bar.
We can use delegate's BeginInvoke
method, when the asynchronous notification is required and the thread from which it originates is not an issue.
References