Option 1: Use ThreadPool.QueueUserWorkItem
Depending on the size of the job to be processed, I always admire the ThreadPool.QueueUserWorkItem; this creates a handy pool of threads, and execute the process upon request whenever a thread is idle'ly availble in the pool.
using System; using System.Threading; public class Example { public static void Main() { // Queue the task. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc)); Console.WriteLine("Main thread does some work, then sleeps."); // If you comment out the Sleep, the main thread exits before // the thread pool task runs. The thread pool uses background // threads, which do not keep the application running. (This // is a simple example of a race condition.) Thread.Sleep(1000); Console.WriteLine("Main thread exits."); } // This thread procedure performs the task. static void ThreadProc(Object stateInfo) { // No state object was passed to QueueUserWorkItem, so // stateInfo is null. Console.WriteLine("Hello from the thread pool."); } }
Option 2: Implement a custom ThreadPool using BackgroundWorker, incase you are hating ThreadPool for whatever reason.
The main worker object.
///The manager class that manages the worker thread./// The core entity that handles /// public class CWorkers { public int _nIndex { get; private set; } public BackgroundWorker bgWorker { get; private set; } //the main "culprit" public CWorkers(int nIndex) { _nIndex = nIndex; bgWorker = new BackgroundWorker(); } }
////// Manages the worker thread. /// public class CWorkerManager { private List_lstWorkers;//list of worker threads private const int MAXWORKERS = 5;//Max workers you want; change/update or pull it from app.config. public CWorkerManager() { Initialize(); } /// /// Initializes the thread pool - sorta customized threadpool /// private void Initialize() { _lstWorkers = new List();//initialize the list for (int i = 0; i < MAXWORKERS; i++) { _lstWorkers.Add(CreateAWorker(i)); //inits a worker objects and adds to list } } /// /// Looks for a free thread /// ///Returns the thread if found, else nothing. public CWorkers RequestForWorker() { foreach (var theWorker in _lstWorkers) { if (!theWorker.bgWorker.IsBusy) return theWorker; } return null; } ////// Emulate the BCL's .WaitOne() /// public void WaitAndSignalWhenFree() { while (true) { //Loop through the list to find an idle thread foreach (var theWorker in _lstWorkers) { if (!theWorker.bgWorker.IsBusy) return; } Thread.Sleep(1);//This may be a hack; not really recommended as a production code. } } ////// Inits a CWorker object; adds the /// /// ///private static CWorkers CreateAWorker(int nIndex) { var theWorker = new CWorkers(nIndex); theWorker.bgWorker.DoWork += (sender, e) => ((Action)e.Argument).Invoke(); theWorker.bgWorker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Finished worker number:[" + theWorker._nIndex + "]"); return theWorker; } }
The test program:
class Program { private static List_lstWorkers; private const int MAXWORKERS = 5; static CWorkerManager theManager; static void Main(string[] args) { theManager = new CWorkerManager(); ProcessJobs(20000); Console.ReadKey(); } /// /// Simulator that request the Manager for worker threads /// /// private static void ProcessJobs(int nMaxTime) { Random rndRandom = new Random(); DateTime dteStart = DateTime.Now; //Run till the max time. while (DateTime.Now - dteStart < TimeSpan.FromMilliseconds(nMaxTime)) { var theWorker = theManager.RequestForWorker();//Request for a worker if (theWorker != null) { theWorker.bgWorker.RunWorkerAsync(new Action(() => ProcessThis(theWorker._nIndex, rndRandom.Next(1500, 2500)//Generate somethign random ))); } else { Console.WriteLine("All busy, lets wait..."); theManager.WaitAndSignalWhenFree(); } } } ////// Actual method that processes the job. /// /// /// static void ProcessThis(int nIndex, int nTimeout) { Console.WriteLine("Worker {1} starts to work for {0} ms", nTimeout, nIndex); Thread.Sleep(nTimeout); } }
Output:
Happy threading.