Threading in C# using the ThreadPool
Threading is one of those topics that creates a lot of discussion in programming. Threading in C# is quite easy. However using it correctly, especially when you start doing data access or sharing information across threads it becomes difficult to keep everything in sync. For this example I'm going to focus on creating some basic threads that run and do some basic output to show we have concurrent threads running. You could create a thread and start it, then join, etc. I prefer to let .net handle the thread creation and use the ThreadPool to do all the thread management for us.
You can read more about the ThreadPool here:
MSDN Site
I normally don't post entire files but this post is a bit different. Let's start with the program that is running the threads. We create 20 threads and pass them an index and a ManualResetEvent. This is so we know which thread is exiting and we need to keep track of when the thread is done processing. If we don't care about when the thread terminates there is no need for the manual reset event. It's simply there for the WaitHandle.WaitAll call. The code is below.
This code uses a custom class called ThreadCounter. ThreadCounter simply takes and index and a manual reset event. In the code above we tell the ThreadPool to use the ThreadCounter's DoWork method. To use this we need to pass an object into the callback method that could potentially have state information in it that is useful to the thread. We're not doing anything with that information. All this basic example does is sleep for a random time between .5 and 5 seconds. See the code below.
Below are the results from the program being run. As you can see the threads sleep for random times and are finishing in a random order.
I've attached the entire project in a zip file below for anyone to download.
Thread Pool Manager Zip File
You can read more about the ThreadPool here:
MSDN Site
I normally don't post entire files but this post is a bit different. Let's start with the program that is running the threads. We create 20 threads and pass them an index and a ManualResetEvent. This is so we know which thread is exiting and we need to keep track of when the thread is done processing. If we don't care about when the thread terminates there is no need for the manual reset event. It's simply there for the WaitHandle.WaitAll call. The code is below.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ThreadPoolManager
{
class Program
{
static void Main( string[] args )
{
int count = 20;
ManualResetEvent[] doneEvents = new ManualResetEvent[count];
for( int i = 0; i < count; i++ )
{
// create a ManaulResetEvent that is set to fals
// this means it's not done procvess
doneEvents[i] = new ManualResetEvent( false );
// create an instance of our threading helper class
// passing int he index and manual reset event
ThreadCounter threadCounter = new ThreadCounter( i, doneEvents[i] );
// queue the worker items, they will run and terminate automatically
ThreadPool.QueueUserWorkItem( new WaitCallback( threadCounter.DoWork ) );
}
// wait for all the threads to finish
WaitHandle.WaitAll( doneEvents );
// do some output and let the user press the enter key
Console.WriteLine();
Console.WriteLine( "Done processing...Pmsress any key to terminate" );
Console.ReadKey();
}
}
}
This code uses a custom class called ThreadCounter. ThreadCounter simply takes and index and a manual reset event. In the code above we tell the ThreadPool to use the ThreadCounter's DoWork method. To use this we need to pass an object into the callback method that could potentially have state information in it that is useful to the thread. We're not doing anything with that information. All this basic example does is sleep for a random time between .5 and 5 seconds. See the code below.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ThreadPoolManager
{
/// <summary>
/// A basic class used to hold a thread counter index
/// </summary>
public class ThreadCounter
{
#region Fields
private int _index;
private ManualResetEvent _doneEvent;
#endregion
#region Properties
/// <summary>
/// Gets or sets the index.
/// </summary>
/// <value>The index.</value>
public int Index
{
get { return _index; }
set { _index = value; }
}
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="ThreadCounter"/> class.
/// </summary>
public ThreadCounter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ThreadCounter"/> class.
/// </summary>
/// <param name="index">The index.</param>
public ThreadCounter( int index, ManualResetEvent doneEvent )
{
_index = index;
_doneEvent = doneEvent;
}
/// <summary>
/// Does the work.
/// </summary>
public void DoWork( object stateInfo )
{
// we want this thread to sleep for some random amount of time
Random r = new Random();
int sleepTime = r.Next( 500, 5000 );
Thread.Sleep( sleepTime );
Console.WriteLine( this.ToString() );
// set our done event so that any wait handles know we're done
_doneEvent.Set();
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return String.Format( "This is thread {0} exiting.", _index );
}
}
}
Below are the results from the program being run. As you can see the threads sleep for random times and are finishing in a random order.
I've attached the entire project in a zip file below for anyone to download.
Thread Pool Manager Zip File
Comments