65.9K
CodeProject is changing. Read more.
Home

Simple mutlithreaded application

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (9 votes)

Apr 6, 2009

CPOL

2 min read

viewsIcon

37059

downloadIcon

553

A simple mutlithreaded application using ThreadPool.

Introduction

During some performance testing phases, you often discover that there is a lack in the software performance, and that the solution is not responding, or is not completing its tasks in an acceptable time. This can be caused due to many issues, some related to the code efficiency, or external dependencies, or the execution flow.

In this article, we will see an approach that can help in resolving performance issues. Multi-threading can be a good solution. Multithreading is a popular programming and execution model that allows multiple threads to exist within the context of a single process. These threads share the process' resources but are able to execute independently. The threaded programming model provides developers with a useful abstraction of concurrent execution. However, perhaps the most interesting application of the technology is when it is applied to a single process to enable parallel execution on a multiprocessor system.

Background

You should be familiar with Microsoft .NET, and multi-threading implementation in C#.

Using the code

In this example, we will look at a simple multi-threaded application. We will use the ThreadPool class and the class will be responsible for creating threads, and queuing requests to be picked up later by the created threads. Alternatively, you can do your own thread creation, assignment, and disposing.

First, we will start by creating a parent thread, and assign the delegate to be called. The Start function is a void function with no parameters that will be called once the main thread is created.

Thread parent = new Thread(new ThreadStart(Start));
parent.Start();

The Start function is responsible for creating the threads and queuing the items in the ThreadPool.

private void Start()
{
    // maximum threads cannot be less than the number of CPUs
    ThreadPool.SetMaxThreads(int.Parse(txtMaxThreads.Text), int.Parse(txtMaxIOThreads.Text));
    ThreadPool.SetMinThreads(int.Parse(txtMinThreads.Text), int.Parse(txtMinIOThreads.Text));

    int requestCount = Convert.ToInt32(txtNumberOfRequest.Text);
    AutoResetEvent[] _waitAllEvents; // Array of objects to wait upon.

    // Create an array of objects to wait upon; we need one per thread
    _waitAllEvents = new AutoResetEvent[requestCount];
    // Populate array of waiting objects, one for each work item
    for (int i = 0; i < requestCount; i++)
    {
        _waitAllEvents[i] = new AutoResetEvent(false);
    }
    for (int rqCounter = 0; rqCounter < requestCount; rqCounter++)
    {
        WaitCallback callBack = new WaitCallback(InternalCommand);
        ThreadPool.QueueUserWorkItem(callBack, 
          new Pair(rqCounter.ToString(), _waitAllEvents[rqCounter]));
    }

    // Wait until all our threads have signaled their wait object is done.
    if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
    {
        // WaitAll for multiple handles on an STA thread is not supported.
        // ...so wait on each handle individually.
        foreach (WaitHandle myWaitHandle in _waitAllEvents)
            WaitHandle.WaitAny(new WaitHandle[] { myWaitHandle });
        System.Diagnostics.EventLog.WriteEntry("Finished Execution " + 
                           "STA Mode - Logs are : \n\n", result);
    }
    else
    {
        WaitHandle.WaitAll(_waitAllEvents);
        MessageBox.Show("Finished Execution MTA Mode - Logs are : \n\n" + result);
    }
}

InternalCommand is where the real execution happens, where you can add, delete, or do anything. After you finish, you have to inform the ThreadPool that you finished execution. And, that is done by setting the AutoResetEvent.

private void InternalCommand(object state)
{
    string threadID = (string)((Pair)state).First;
    lock (result)
    {
        result += "Thread with the thread id ";
        result += threadID;
        result += " was called";
        result += "\n";
    }
    AutoResetEvent autoResetEvent = ((Pair)state).Second as AutoResetEvent;
    // Set our wait item to indicate we are done here.
    autoResetEvent.Set();
}

Example execution flow

This example execution flow can be described as follows:

  1. The ThreadPool will be initialized based on the user input.
  2. Based on the request count, the request event handlers will be created.
  3. All request will be queued into the ThreadPool queue, where the ThreadPool will create working threads to handle the requests.
  4. At the execution function, whenever it finishes execution, the handler will be set.
  5. When all event handlers are set, a message box will be shown.

History

  • Version 1.0.