// 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
}