Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET multi-threading and communication between threads

0.00/5 (No votes)
29 Dec 2002 2  
An article that explains the messaging between main and worker threads

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.
// Specify no of worker threads to start

_ThreadMgr = new ThreadManager(Convert.ToInt32
    (ThreadCount.Value.ToString()));

MethodNoParam _RunMethod = new 
    MethodNoParam(this.ThreadMehod);
MethodWithParam _ReceiveMethod = new 
    MethodWithParam(this.CallBackMethod);

// Code executed by Worker threads			

_ThreadMgr.SetRunMethod(_RunMethod);

// Message receiving method- Main thread 

_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});
    //System.Diagnostics.Debug.WriteLine

    //    (Thread.CurrentThread.Name);

}

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

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