Click here to Skip to main content
15,881,600 members
Articles / General Programming / Threads

Smart Thread Pool

Rate me:
Please Sign up or sign in to vote.
4.96/5 (314 votes)
27 Aug 2012Ms-PL40 min read 2.2M   29.1K   1.1K  
A .NET Thread Pool fully implemented in C# with many features.
// Ami Bar
// amibar@gmail.com
//
// Smart Thread Pool in C#.
// 7 Aug 2004 - Initial release
// 14 Sep 2004 - Bug fixes 
// 15 Oct 2004 - Added new features
//		- Work items return result.
//		- Support waiting synchronization for multiple work items.
//		- Work items can be cancelled.
//		- Passage of the caller thread�s context to the thread in the pool.
//		- Minimal usage of WIN32 handles.
//		- Minor bug fixes.
// 26 Dec 2004 - Changes:
//		- Removed static constructors.
//      - Added finalizers.
//		- Changed Exceptions so they are serializable.
//		- Fixed the bug in one of the SmartThreadPool constructors.
//		- Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. 
//        The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
//		- Added PostExecute with options on which cases to call it.
//      - Added option to dispose of the state objects.
//      - Added a WaitForIdle() method that waits until the work items queue is empty.
//      - Added an STPStartInfo class for the initialization of the thread pool.
//      - Changed exception handling so if a work item throws an exception it 
//        is rethrown at GetResult(), rather then firing an UnhandledException event.
//        Note that PostExecute exception are always ignored.
// 25 Mar 2005 - Fixed bug:
//		- Fixed bug where work items got lost. It could happen sometimes, but especially 
//        when the idle timeout is small.
// 3 Jul 2005 - Fixed bug:
//		- Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, 
//		  hardly reconstructed
// 16 Aug 2005 - Fixed bug:
//		- Fixed bug where the InUseThreads becomes negative when cancelling work items


using System;
using System.Security;
using System.Threading;
using System.Collections;
using System.Diagnostics;

namespace Amib.Threading
{
	#region CallToPostExecute enumerator

    [Flags]
	public enum CallToPostExecute
	{
        Never                    = 0x00,
        WhenWorkItemCanceled     = 0x01,
		WhenWorkItemNotCanceled  = 0x02,
		Always                   = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
	}

	#endregion

	#region SmartThreadPool class
	/// <summary>
	/// Smart thread pool class.
	/// </summary>
    public class SmartThreadPool : IDisposable
    {
        #region Default Constants

        /// <summary>
        /// Default minimum number of threads the thread pool contains. (0)
        /// </summary>
        public const int DefaultMinWorkerThreads = 0;

        /// <summary>
        /// Default maximum number of threads the thread pool contains. (25)
        /// </summary>
        public const int DefaultMaxWorkerThreads = 25;

        /// <summary>
        /// Default idle timeout in milliseconds. (One minute)
        /// </summary>
        public const int DefaultIdleTimeout = 60*1000; // One minute

        /// <summary>
        /// Indicate to copy the security context of the caller and then use it in the call. (false)
        /// </summary>
        public const bool DefaultUseCallerContext = false; 

        /// <summary>
        /// Indicate to dispose of the state objects if they support the IDispose interface. (false)
        /// </summary>
        public const bool DefaultDisposeOfStateObjects = false; 

        /// <summary>
        /// The default option to run the post execute
        /// </summary>
        public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;

		/// <summary>
		/// The default post execute method to run. 
		/// When null it means not to call it.
		/// </summary>
		public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null;

        #endregion

        #region Member Variables

        /// <summary>
        /// Hashtable of all the threads in the thread pool.
        /// </summary>
        private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable());

        /// <summary>
        /// Queue of work items.
        /// </summary>
        private WorkItemsQueue _workItemsQueue = new WorkItemsQueue();

        /// <summary>
        /// Number of threads that currently work (not idle).
        /// </summary>
        private int _inUseWorkerThreads = 0;

        /// <summary>
        /// Start information to use. 
        /// It is simpler than providing many constructors.
        /// </summary>
        private STPStartInfo _stpStartInfo = new STPStartInfo();

        /// <summary>
        /// Total number of work items that are stored in the work items queue 
        /// plus the work items that the threads in the pool are working on.
        /// </summary>
        private int _currentWorkItemsCount = 0;

        /// <summary>
        /// Signaled when the thread pool is idle, i.e. no thread is busy
        /// and the work items queue is empty
        /// </summary>
        private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);

        /// <summary>
        /// An event to signal all the threads to quit immediately.
        /// </summary>
        private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);

        /// <summary>
        /// A flag to indicate the threads to quit.
        /// </summary>
        private bool _shutdown = false;

        /// <summary>
        /// Counts the threads created in the pool.
        /// It is used to name the threads.
        /// </summary>
        private int _threadCounter = 0;

        /// <summary>
        /// Indicate that the SmartThreadPool has been disposed
        /// </summary>
        private bool _isDisposed = false;

        #endregion

        #region Construction and Finalization

        /// <summary>
        /// Constructor
        /// </summary>
        public SmartThreadPool()
        {
            Start();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="idleTimeout">Idle timeout in milliseconds</param>
        public SmartThreadPool(int idleTimeout)
        {
            _stpStartInfo.IdleTimeout = idleTimeout;
            Start();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="idleTimeout">Idle timeout in milliseconds</param>
        /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
        public SmartThreadPool(
            int idleTimeout,
            int maxWorkerThreads)
        {
            _stpStartInfo.IdleTimeout = idleTimeout;
            _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
            Start();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="idleTimeout">Idle timeout in milliseconds</param>
        /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
        /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
        public SmartThreadPool(
            int idleTimeout,
            int maxWorkerThreads,
            int minWorkerThreads)
        {
            _stpStartInfo.IdleTimeout = idleTimeout;
            _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
            _stpStartInfo.MinWorkerThreads = minWorkerThreads;
            Start();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="idleTimeout">Idle timeout in milliseconds</param>
        /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
        /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
        /// <param name="useCallerContext">Indicate to copy the security context of the caller and then use it in the call</param>
        /// <param name="disposeOfStateObjects">Indicate to dispose of the state objects if they support the IDispose interface</param>
        public SmartThreadPool(STPStartInfo stpStartInfo)
        {
            _stpStartInfo = new STPStartInfo(stpStartInfo);
            Start();
        }

        private void Start()
        {
            ValidateSTPStartInfo();
            StartThreads(_stpStartInfo.MinWorkerThreads);
        }

        private void ValidateSTPStartInfo()
        {
            if (_stpStartInfo.MinWorkerThreads < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "MinWorkerThreads", "MinWorkerThreads cannot be negative");
            }

            if (_stpStartInfo.MaxWorkerThreads <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
            }

            if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
            {
                throw new ArgumentOutOfRangeException(
                    "MinWorkerThreads, maxWorkerThreads", 
                    "MaxWorkerThreads must be greater or equal to MinWorkerThreads");
            }
        }

        #endregion

        #region Thread Processing

        /// <summary>
        /// Waits on the queue for a work item, shutdown, or timeout.
        /// </summary>
        /// <returns>
        /// Returns the WaitingCallback or null in case of timeout or shutdown.
        /// </returns>
        private WorkItem Dequeue()
        {
            WorkItem workItem = 
                _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent);

            return workItem;
        }

        /// <summary>
        /// Put a new work item in the queue
        /// </summary>
        /// <param name="workItem">A work item to queue</param>
        private void Enqueue(WorkItem workItem)
        {
            // Make sure the workItem is not null
            Debug.Assert(null != workItem);

            IncrementWorkItemsCount();

            _workItemsQueue.EnqueueWorkItem(workItem);

            // If all the threads are busy then try to create a new one
            if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) 
            {
                StartThreads(1);
            }
        }

        private void IncrementWorkItemsCount()
        {
            int count = Interlocked.Increment(ref _currentWorkItemsCount);
            if (count == 1) 
            {
                _isIdleWaitHandle.Reset();
            }
        }

        private void DecrementWorkItemsCount()
        {
            int count = Interlocked.Decrement(ref _currentWorkItemsCount);
            if (count == 0) 
            {
                _isIdleWaitHandle.Set();
            }
        }

        /// <summary>
        /// Inform that the current thread is about to quit or quiting.
        /// The same thread may call this method more than once.
        /// </summary>
        private void InformCompleted()
        {
            // There is no need to lock the two methods together 
            // since only the current thread removes itself
            // and the _workerThreads is a synchronized hashtable
            if (_workerThreads.Contains(Thread.CurrentThread))
            {
                _workerThreads.Remove(Thread.CurrentThread);
            }
        }

        /// <summary>
        /// Starts new threads
        /// </summary>
        /// <param name="threadsCount">The number of threads to start</param>
        private void StartThreads(int threadsCount)
        {
            lock(_workerThreads.SyncRoot)
            {
                // Don't start threads on shut down
                if (_shutdown)
                {
                    return;
                }

                for(int i = 0; i < threadsCount; ++i)
                {
                    // Don't create more threads then the upper limit
                    if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
                    {
                        return;
                    }

                    // Create a new thread
                    Thread workerThread = new Thread(new ThreadStart(ProcessQueuedItems));

                    // Configure the new thread and start it
                    workerThread.Name = "STP Thread #" + _threadCounter;
                    workerThread.IsBackground = true;
                    workerThread.Start();
                    ++_threadCounter;

                    // Add the new thread to the hashtable and update its creation
                    // time.
                    _workerThreads[workerThread] = DateTime.Now;
                }
            }
        }

        /// <summary>
        /// A worker thread method that processes work items from the work items queue.
        /// </summary>
        private void ProcessQueuedItems()
        {
            try
            {
				bool bInUseWorkerThreadsWasIncremented = false;

                // Process until shutdown.
                while(!_shutdown)
                {
                    // Update the last time this thread was seen alive.
                    // It's good for debugging.
                    _workerThreads[Thread.CurrentThread] = DateTime.Now;

                    // Wait for a work item, shutdown, or timeout
                    WorkItem workItem = Dequeue();

                    // Update the last time this thread was seen alive.
                    // It's good for debugging.
                    _workerThreads[Thread.CurrentThread] = DateTime.Now;

                    // On timeout or shut down.
                    if (null == workItem)
                    {
                        // Double lock for quit.
                        if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
                        {
                            lock(_workerThreads.SyncRoot)
                            {
                                if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
                                {
                                    // Inform that the thread is quiting and then quit.
                                    // This method must be called within this lock or else
                                    // more threads will quit and the thread pool will go
                                    // below the lower limit.
                                    InformCompleted();
                                    break;
                                }
                            }
                        }
                    }

                    // If we didn't quit then skip to the next iteration.
                    if (null == workItem)
                    {
                        continue;
                    }

                    try 
                    {
						// Initialize the value to false
						bInUseWorkerThreadsWasIncremented = false;

                        // Change the state of the work item to 'in progress' if possible.
                        // We do it here so if the work item has been canceled we won't 
                        // increment the _inUseWorkerThreads.
                        // The cancel mechanism doesn't delete items from the queue,  
                        // it marks the work item as canceled, and when the work item
                        // is dequeued, we just skip it.
                        // If the post execute of work item is set to always or to
                        // call when the work item is canceled then the StartingWorkItem()
                        // will return true, so the post execute can run.
                        if (!workItem.StartingWorkItem())
                        {
                            continue;
                        }

                        // Execute the callback.  Make sure to accurately
                        // record how many callbacks are currently executing.
						Interlocked.Increment(ref _inUseWorkerThreads);

						// Mark that the _inUseWorkerThreads incremented, so in the finally{}
						// statement we will decrement it correctly.
						bInUseWorkerThreadsWasIncremented = true;

                        workItem.Execute();
                    }
                    finally
                    {
                        if (_stpStartInfo.DisposeOfStateObjects)
                        {
                            workItem.Dispose();
                        }

						// Decrement the _inUseWorkerThreads only if we had 
						// incremented it. Note the cancelled work items don't
						// increment _inUseWorkerThreads.
						if (bInUseWorkerThreadsWasIncremented)
						{
							Interlocked.Decrement(ref _inUseWorkerThreads);
						}

                        DecrementWorkItemsCount();
                    }
                }
            } 
            catch(ThreadAbortException tae)
            {
                tae = tae;
                // Handle the abort exception gracfully.
                Thread.ResetAbort();
            }
            catch(Exception e)
            {
                Debug.Assert(null != e);
            }
            finally
            {
                InformCompleted();
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Queues a user work item to the thread pool.
        /// </summary>
        /// <param name="callback">
        /// A WaitCallback representing the delegate to invoke when the thread in the 
        /// thread pool picks up the work item.
        /// </param>
        public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
        {
            // Queue the delegate with no state
            return QueueWorkItem(callback, null);
        }

        /// <summary>
        /// Queues a user work item to the thread pool.
        /// </summary>
        /// <param name="callback">
        /// A WaitCallback representing the delegate to invoke when the thread in the 
        /// thread pool picks up the work item.
        /// </param>
        /// <param name="state">
        /// The object that is passed to the delegate when serviced from the thread pool.
        /// </param>
        public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
        {
            ValidateNotDisposed();
            // Create a work item that contains the delegate and its state.
            WorkItem workItem = new WorkItem(
				callback, 
				state, 
				_stpStartInfo.UseCallerContext, 
				_stpStartInfo.PostExecuteWorkItemCallback, 
				_stpStartInfo.CallToPostExecute);
            Enqueue(workItem);
            return workItem.GetWorkItemResult();
        }

        /// <summary>
        /// Queues a user work item to the thread pool.
        /// </summary>
        /// <param name="callback">
        /// A WaitCallback representing the delegate to invoke when the thread in the 
        /// thread pool picks up the work item.
        /// </param>
        /// <param name="state">
        /// The object that is passed to the delegate when serviced from the thread pool.
        /// </param>
        /// <param name="postExecuteWorkItemCallback">
        /// A delegate to call after the callback completion
        /// </param>
        /// <returns>Work item result</returns>
        public IWorkItemResult QueueWorkItem(
            WorkItemCallback callback, 
            object state,
            PostExecuteWorkItemCallback postExecuteWorkItemCallback)
        {
            ValidateNotDisposed();

            // Create a work item that contains the delegate and its state.
            WorkItem workItem = new WorkItem(
                callback, 
                state, 
                _stpStartInfo.UseCallerContext, 
                postExecuteWorkItemCallback,
                _stpStartInfo.CallToPostExecute);
            Enqueue(workItem);
            return workItem.GetWorkItemResult();
        }

        /// <summary>
        /// Queues a user work item to the thread pool.
        /// </summary>
        /// <param name="callback">
        /// A WaitCallback representing the delegate to invoke when the thread in the 
        /// thread pool picks up the work item.
        /// </param>
        /// <param name="state">
        /// The object that is passed to the delegate when serviced from the thread pool.
        /// </param>
        /// <param name="postExecuteWorkItemCallback">
        /// A delegate to call after the callback completion
        /// </param>
        /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
        /// <returns>Work item result</returns>
        public IWorkItemResult QueueWorkItem(
            WorkItemCallback callback, 
            object state,
            PostExecuteWorkItemCallback postExecuteWorkItemCallback,
            CallToPostExecute callToPostExecute)
        {
            ValidateNotDisposed();

            // Create a work item that contains the delegate and its state.
            WorkItem workItem = new WorkItem(
                callback, 
                state, 
                _stpStartInfo.UseCallerContext, 
                postExecuteWorkItemCallback,
                callToPostExecute);
            Enqueue(workItem);
            return workItem.GetWorkItemResult();
        }

        /// <summary>
        /// Wait for the thread pool to be idle
        /// </summary>
        public void WaitForIdle()
        {
            WaitForIdle(Timeout.Infinite);
        }

        /// <summary>
        /// Wait for the thread pool to be idle
        /// </summary>
        public bool WaitForIdle(TimeSpan timeout)
        {
            return WaitForIdle((int)timeout.TotalMilliseconds);
        }

        /// <summary>
        /// Wait for the thread pool to be idle
        /// </summary>
        public bool WaitForIdle(int millisecondsTimeout)
        {
            return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
        }

        /// <summary>
        /// Force the SmartThreadPool to shutdown
        /// </summary>
        public void Shutdown()
        {
            Shutdown(true, 0);
        }

        public void Shutdown(bool forceAbort, TimeSpan timeout)
        {
            Shutdown(forceAbort, (int)timeout.TotalMilliseconds);
        }

        /// <summary>
        /// Empties the queue of work items and abort the threads in the pool.
        /// </summary>
        public void Shutdown(bool forceAbort, int millisecondsTimeout)
        {
            ValidateNotDisposed();
            Thread [] threads = null;
            lock(_workerThreads.SyncRoot)
            {
                // Shutdown the work items queue
                _workItemsQueue.Dispose();

                // Signal the threads to exit
                _shutdown = true;
                _shuttingDownEvent.Set();

                // Make a copy of the threads' references in the pool
                threads = new Thread [_workerThreads.Count];
                _workerThreads.Keys.CopyTo(threads, 0);
            }

            int millisecondsLeft = millisecondsTimeout;
            DateTime start = DateTime.Now;
            bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
            bool timeout = false;

            // Each iteration we update the time left for the timeout.
            foreach(Thread thread in threads)
            {
                // Join don't work with negative numbers
                if (!waitInfinitely && (millisecondsLeft < 0))
                {
                    timeout = true;
                    break;
                }

                bool success = thread.Join(millisecondsLeft);
                if(!success)
                {
                    timeout = true;
                    break;
                }

                if(!waitInfinitely)
                {
                    // Update the time left to wait
                    TimeSpan ts = DateTime.Now - start;
                    millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
                }
            }

            if (timeout && forceAbort)
            {
                // Abort the threads in the pool
                foreach(Thread thread in threads)
                {
                    if ((thread != null) && thread.IsAlive) 
                    {
						try 
						{
							thread.Abort("Shutdown");
						}
						catch(SecurityException e)
						{
							e = e;
						}
						catch(ThreadStateException ex)
						{
							ex = ex;
							// In case the thread has been terminated 
							// after the check if it is alive.
						}
                    }
                }
            }
        }

        /// <summary>
        /// Wait for all work items to complete
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <returns>
        /// true when every work item in workItemResults has completed; otherwise false.
        /// </returns>
        public static bool WaitAll(
            IWorkItemResult [] workItemResults)
        {
            return WaitAll(workItemResults, Timeout.Infinite, true);
        }

        /// <summary>
        /// Wait for all work items to complete
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <returns>
        /// true when every work item in workItemResults has completed; otherwise false.
        /// </returns>
        public static bool WaitAll(
            IWorkItemResult [] workItemResults,
            TimeSpan timeout,
            bool exitContext)
        {
            return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
        }

        /// <summary>
        /// Wait for all work items to complete
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
        /// <returns>
        /// true when every work item in workItemResults has completed; otherwise false.
        /// </returns>
        public static bool WaitAll(
            IWorkItemResult [] workItemResults,  
            TimeSpan timeout,
            bool exitContext,
            WaitHandle cancelWaitHandle)
        {
            return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
        }

        /// <summary>
        /// Wait for all work items to complete
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <returns>
        /// true when every work item in workItemResults has completed; otherwise false.
        /// </returns>
        public static bool WaitAll(
            IWorkItemResult [] workItemResults,  
            int millisecondsTimeout,
            bool exitContext)
        {
            return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null);
        }

        /// <summary>
        /// Wait for all work items to complete
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
        /// <returns>
        /// true when every work item in workItemResults has completed; otherwise false.
        /// </returns>
        public static bool WaitAll(
            IWorkItemResult [] workItemResults,  
            int millisecondsTimeout,
            bool exitContext,
            WaitHandle cancelWaitHandle)
        {
            return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
        }


        /// <summary>
        /// Waits for any of the work items in the specified array to complete, cancel, or timeout
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <returns>
        /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
        /// </returns>
        public static int WaitAny(
            IWorkItemResult [] workItemResults)
        {
            return WaitAny(workItemResults, Timeout.Infinite, true);
        }

        /// <summary>
        /// Waits for any of the work items in the specified array to complete, cancel, or timeout
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <returns>
        /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
        /// </returns>
        public static int WaitAny(
            IWorkItemResult [] workItemResults,
            TimeSpan timeout,
            bool exitContext)
        {
            return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
        }

        /// <summary>
        /// Waits for any of the work items in the specified array to complete, cancel, or timeout
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
        /// <returns>
        /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
        /// </returns>
        public static int WaitAny(
            IWorkItemResult [] workItemResults,
            TimeSpan timeout,
            bool exitContext,
            WaitHandle cancelWaitHandle)
        {
            return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
        }

        /// <summary>
        /// Waits for any of the work items in the specified array to complete, cancel, or timeout
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <returns>
        /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
        /// </returns>
        public static int WaitAny(
            IWorkItemResult [] workItemResults,  
            int millisecondsTimeout,
            bool exitContext)
        {
            return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null);
        }

        /// <summary>
        /// Waits for any of the work items in the specified array to complete, cancel, or timeout
        /// </summary>
        /// <param name="workItemResults">Array of work item result objects</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
        /// <param name="exitContext">
        /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
        /// </param>
        /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
        /// <returns>
        /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
        /// </returns>
        public static int WaitAny(
            IWorkItemResult [] workItemResults,  
            int millisecondsTimeout,
            bool exitContext,
            WaitHandle cancelWaitHandle)
        {
            return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Get the lower limit of threads in the pool.
        /// </summary>
        public int MinThreads 
        { 
            get 
            {
                ValidateNotDisposed();
                return _stpStartInfo.MinWorkerThreads; 
            }
        }

        /// <summary>
        /// Get the upper limit of threads in the pool.
        /// </summary>
        public int MaxThreads 
        { 
            get 
            {
                ValidateNotDisposed();
                return _stpStartInfo.MaxWorkerThreads; 
            } 
        }
        /// <summary>
        /// Get the number of threads in the thread pool.
        /// Should be between the lower and the upper limits.
        /// </summary>
        public int ActiveThreads 
        { 
            get 
            {
                ValidateNotDisposed();
                return _workerThreads.Count; 
            } 
        }

        /// <summary>
        /// Get the number of busy (not idle) threads in the thread pool.
        /// </summary>
        public int InUseThreads 
        { 
            get 
            { 
                ValidateNotDisposed();
                return _inUseWorkerThreads; 
            } 
        }

        /// <summary>
        /// Get the number of work items in the queue.
        /// </summary>
        public int WaitingCallbacks 
        { 
            get 
            { 
                ValidateNotDisposed();
                return _workItemsQueue.Count;
            } 
        }

        #endregion

        #region IDisposable Members

        ~SmartThreadPool()
        {
            Dispose();
        }

        public void Dispose()
        {
            if (!_isDisposed)
            {
                if (!_shutdown)
                {
                    Shutdown();
                }

                if (null != _shuttingDownEvent)
                {
                
                    _shuttingDownEvent.Close();
                    _shuttingDownEvent = null;
                }
                _workerThreads.Clear();
                _isDisposed = true;
                GC.SuppressFinalize(this);
            }
        }

        private void ValidateNotDisposed()
        {
            if(_isDisposed)
            {
                throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
            }
        }
        #endregion
    }
	#endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior)
Israel Israel
B.Sc. in Computer Science.
Works as Software Developer.

Comments and Discussions