1439 lines
53 KiB
C#
1439 lines
53 KiB
C#
|
// 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<61>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 - Changes:
|
|||
|
// - Fixed lost of work items bug
|
|||
|
// 3 Jul 2005: Changes.
|
|||
|
// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed.
|
|||
|
// 16 Aug 2005: Changes.
|
|||
|
// - Fixed bug where the InUseThreads becomes negative when canceling work items.
|
|||
|
//
|
|||
|
// 31 Jan 2006 - Changes:
|
|||
|
// - Added work items priority
|
|||
|
// - Removed support of chained delegates in callbacks and post executes (nobody really use this)
|
|||
|
// - Added work items groups
|
|||
|
// - Added work items groups idle event
|
|||
|
// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
|
|||
|
// it returns true rather then throwing an exception.
|
|||
|
// - Added option to start the STP and the WIG as suspended
|
|||
|
// - Exception behavior changed, the real exception is returned by an
|
|||
|
// inner exception
|
|||
|
// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.)
|
|||
|
// - Added performance counters
|
|||
|
// - Added priority to the threads in the pool
|
|||
|
//
|
|||
|
// 13 Feb 2006 - Changes:
|
|||
|
// - Added a call to the dispose of the Performance Counter so
|
|||
|
// their won't be a Performance Counter leak.
|
|||
|
// - Added exception catch in case the Performance Counters cannot
|
|||
|
// be created.
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Security;
|
|||
|
using System.Threading;
|
|||
|
using System.Collections;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Runtime.CompilerServices;
|
|||
|
|
|||
|
using Amib.Threading.Internal;
|
|||
|
|
|||
|
namespace Amib.Threading
|
|||
|
{
|
|||
|
#region SmartThreadPool class
|
|||
|
/// <summary>
|
|||
|
/// Smart thread pool class.
|
|||
|
/// </summary>
|
|||
|
public class SmartThreadPool : IWorkItemsGroup, 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 DefaultUseCallerCallContext = false;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
|
|||
|
/// </summary>
|
|||
|
public const bool DefaultUseCallerHttpContext = 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;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The default work item priority
|
|||
|
/// </summary>
|
|||
|
public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The default is to work on work items as soon as they arrive
|
|||
|
/// and not to wait for the start.
|
|||
|
/// </summary>
|
|||
|
public const bool DefaultStartSuspended = false;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The default is not to use the performance counters
|
|||
|
/// </summary>
|
|||
|
public static readonly string DefaultPerformanceCounterInstanceName = null;
|
|||
|
|
|||
|
public static readonly int DefaultStackSize = 0;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The default thread priority
|
|||
|
/// </summary>
|
|||
|
public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Member Variables
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Contains the name of this instance of SmartThreadPool.
|
|||
|
/// Can be changed by the user.
|
|||
|
/// </summary>
|
|||
|
private string _name = "SmartThreadPool";
|
|||
|
|
|||
|
/// <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>
|
|||
|
/// Count the work items handled.
|
|||
|
/// Used by the performance counter.
|
|||
|
/// </summary>
|
|||
|
private long _workItemsProcessed = 0;
|
|||
|
|
|||
|
/// <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;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Event to send that the thread pool is idle
|
|||
|
/// </summary>
|
|||
|
private event EventHandler _stpIdle;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// On idle event
|
|||
|
/// </summary>
|
|||
|
//private event WorkItemsGroupIdleHandler _onIdle;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Holds all the WorkItemsGroup instaces that have at least one
|
|||
|
/// work item int the SmartThreadPool
|
|||
|
/// This variable is used in case of Shutdown
|
|||
|
/// </summary>
|
|||
|
private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable());
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A reference from each thread in the thread pool to its SmartThreadPool
|
|||
|
/// object container.
|
|||
|
/// With this variable a thread can know whatever it belongs to a
|
|||
|
/// SmartThreadPool.
|
|||
|
/// </summary>
|
|||
|
[ThreadStatic]
|
|||
|
private static SmartThreadPool _smartThreadPool;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A reference to the current work item a thread from the thread pool
|
|||
|
/// is executing.
|
|||
|
/// </summary>
|
|||
|
[ThreadStatic]
|
|||
|
private static WorkItem _currentWorkItem;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// STP performance counters
|
|||
|
/// </summary>
|
|||
|
private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Construction and Finalization
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructor
|
|||
|
/// </summary>
|
|||
|
public SmartThreadPool()
|
|||
|
{
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructor
|
|||
|
/// </summary>
|
|||
|
/// <param name="idleTimeout">Idle timeout in milliseconds</param>
|
|||
|
public SmartThreadPool(int idleTimeout)
|
|||
|
{
|
|||
|
_stpStartInfo.IdleTimeout = idleTimeout;
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
/// <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;
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
/// <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;
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructor
|
|||
|
/// </summary>
|
|||
|
public SmartThreadPool(STPStartInfo stpStartInfo)
|
|||
|
{
|
|||
|
_stpStartInfo = new STPStartInfo(stpStartInfo);
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
private void Initialize()
|
|||
|
{
|
|||
|
ValidateSTPStartInfo();
|
|||
|
|
|||
|
if (null != _stpStartInfo.PerformanceCounterInstanceName)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
_pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
|
|||
|
}
|
|||
|
catch(Exception e)
|
|||
|
{
|
|||
|
Debug.WriteLine("Unable to create Performance Counters: " + e.ToString());
|
|||
|
_pcs = NullSTPInstancePerformanceCounters.Instance;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
StartOptimalNumberOfThreads();
|
|||
|
}
|
|||
|
|
|||
|
private void StartOptimalNumberOfThreads()
|
|||
|
{
|
|||
|
int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
|
|||
|
threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
|
|||
|
StartThreads(threadsCount);
|
|||
|
}
|
|||
|
|
|||
|
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");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void ValidateCallback(Delegate callback)
|
|||
|
{
|
|||
|
if(callback.GetInvocationList().Length > 1)
|
|||
|
{
|
|||
|
throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#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)
|
|||
|
{
|
|||
|
Enqueue(workItem, true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Put a new work item in the queue
|
|||
|
/// </summary>
|
|||
|
/// <param name="workItem">A work item to queue</param>
|
|||
|
internal void Enqueue(WorkItem workItem, bool incrementWorkItems)
|
|||
|
{
|
|||
|
// Make sure the workItem is not null
|
|||
|
Debug.Assert(null != workItem);
|
|||
|
|
|||
|
if (incrementWorkItems)
|
|||
|
{
|
|||
|
IncrementWorkItemsCount();
|
|||
|
}
|
|||
|
|
|||
|
_workItemsQueue.EnqueueWorkItem(workItem);
|
|||
|
workItem.WorkItemIsQueued();
|
|||
|
|
|||
|
// If all the threads are busy then try to create a new one
|
|||
|
if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count)
|
|||
|
{
|
|||
|
StartThreads(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void IncrementWorkItemsCount()
|
|||
|
{
|
|||
|
_pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
|
|||
|
|
|||
|
int count = Interlocked.Increment(ref _currentWorkItemsCount);
|
|||
|
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
|
|||
|
if (count == 1)
|
|||
|
{
|
|||
|
//Trace.WriteLine("STP is NOT idle");
|
|||
|
_isIdleWaitHandle.Reset();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void DecrementWorkItemsCount()
|
|||
|
{
|
|||
|
++_workItemsProcessed;
|
|||
|
|
|||
|
// The counter counts even if the work item was cancelled
|
|||
|
_pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
|
|||
|
|
|||
|
int count = Interlocked.Decrement(ref _currentWorkItemsCount);
|
|||
|
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
|
|||
|
if (count == 0)
|
|||
|
{
|
|||
|
//Trace.WriteLine("STP is idle");
|
|||
|
_isIdleWaitHandle.Set();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
|
|||
|
{
|
|||
|
_workItemsGroups[workItemsGroup] = workItemsGroup;
|
|||
|
}
|
|||
|
|
|||
|
internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
|
|||
|
{
|
|||
|
if (_workItemsGroups.Contains(workItemsGroup))
|
|||
|
{
|
|||
|
_workItemsGroups.Remove(workItemsGroup);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <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);
|
|||
|
_pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Starts new threads
|
|||
|
/// </summary>
|
|||
|
/// <param name="threadsCount">The number of threads to start</param>
|
|||
|
private void StartThreads(int threadsCount)
|
|||
|
{
|
|||
|
if (_stpStartInfo.StartSuspended)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
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), _stpStartInfo.StackSize);
|
|||
|
|
|||
|
// Configure the new thread and start it
|
|||
|
workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
|
|||
|
workerThread.IsBackground = true;
|
|||
|
workerThread.Priority = _stpStartInfo.ThreadPriority;
|
|||
|
workerThread.Start();
|
|||
|
++_threadCounter;
|
|||
|
|
|||
|
// Add the new thread to the hashtable and update its creation
|
|||
|
// time.
|
|||
|
_workerThreads[workerThread] = DateTime.Now;
|
|||
|
_pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A worker thread method that processes work items from the work items queue.
|
|||
|
/// </summary>
|
|||
|
private void ProcessQueuedItems()
|
|||
|
{
|
|||
|
// Initialize the _smartThreadPool variable
|
|||
|
_smartThreadPool = this;
|
|||
|
|
|||
|
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.
|
|||
|
int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads);
|
|||
|
_pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
|
|||
|
|
|||
|
// Mark that the _inUseWorkerThreads incremented, so in the finally{}
|
|||
|
// statement we will decrement it correctly.
|
|||
|
bInUseWorkerThreadsWasIncremented = true;
|
|||
|
|
|||
|
// Set the _currentWorkItem to the current work item
|
|||
|
_currentWorkItem = workItem;
|
|||
|
|
|||
|
lock(workItem)
|
|||
|
{
|
|||
|
workItem.currentThread = Thread.CurrentThread;
|
|||
|
}
|
|||
|
|
|||
|
ExecuteWorkItem(workItem);
|
|||
|
|
|||
|
lock(workItem)
|
|||
|
{
|
|||
|
workItem.currentThread = null;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
catch(ThreadAbortException ex)
|
|||
|
{
|
|||
|
lock(workItem)
|
|||
|
{
|
|||
|
workItem.currentThread = null;
|
|||
|
}
|
|||
|
ex.GetHashCode();
|
|||
|
Thread.ResetAbort();
|
|||
|
}
|
|||
|
catch(Exception ex)
|
|||
|
{
|
|||
|
ex.GetHashCode();
|
|||
|
// Do nothing
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
lock(workItem)
|
|||
|
{
|
|||
|
workItem.currentThread = null;
|
|||
|
}
|
|||
|
|
|||
|
if (null != workItem)
|
|||
|
{
|
|||
|
workItem.DisposeOfState();
|
|||
|
}
|
|||
|
|
|||
|
// Set the _currentWorkItem to null, since we
|
|||
|
// no longer run user's code.
|
|||
|
_currentWorkItem = null;
|
|||
|
|
|||
|
// Decrement the _inUseWorkerThreads only if we had
|
|||
|
// incremented it. Note the cancelled work items don't
|
|||
|
// increment _inUseWorkerThreads.
|
|||
|
if (bInUseWorkerThreadsWasIncremented)
|
|||
|
{
|
|||
|
int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads);
|
|||
|
_pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
|
|||
|
}
|
|||
|
|
|||
|
// Notify that the work item has been completed.
|
|||
|
// WorkItemsGroup may enqueue their next work item.
|
|||
|
workItem.FireWorkItemCompleted();
|
|||
|
|
|||
|
// Decrement the number of work items here so the idle
|
|||
|
// ManualResetEvent won't fluctuate.
|
|||
|
DecrementWorkItemsCount();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch(ThreadAbortException tae)
|
|||
|
{
|
|||
|
tae.GetHashCode();
|
|||
|
// Handle the abort exception gracfully.
|
|||
|
Thread.ResetAbort();
|
|||
|
}
|
|||
|
catch(Exception e)
|
|||
|
{
|
|||
|
Debug.Assert(null != e);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
InformCompleted();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void ExecuteWorkItem(WorkItem workItem)
|
|||
|
{
|
|||
|
_pcs.SampleWorkItemsWaitTime(workItem.WaitingTime);
|
|||
|
try
|
|||
|
{
|
|||
|
workItem.Execute();
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
throw;
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
_pcs.SampleWorkItemsProcessTime(workItem.ProcessTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Public Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="workItemPriority">The priority of the work item</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="workItemInfo">Work item info</param>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </param>
|
|||
|
/// <param name="workItemPriority">The work item priority</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="workItemInfo">Work item information</param>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </param>
|
|||
|
/// <param name="postExecuteWorkItemCallback">
|
|||
|
/// A delegate to call after the callback completion
|
|||
|
/// </param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(
|
|||
|
WorkItemCallback callback,
|
|||
|
object state,
|
|||
|
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </param>
|
|||
|
/// <param name="postExecuteWorkItemCallback">
|
|||
|
/// A delegate to call after the callback completion
|
|||
|
/// </param>
|
|||
|
/// <param name="workItemPriority">The work item priority</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(
|
|||
|
WorkItemCallback callback,
|
|||
|
object state,
|
|||
|
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
|||
|
WorkItemPriority workItemPriority)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </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>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(
|
|||
|
WorkItemCallback callback,
|
|||
|
object state,
|
|||
|
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
|||
|
CallToPostExecute callToPostExecute)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
|
|||
|
Enqueue(workItem);
|
|||
|
return workItem.GetWorkItemResult();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Queue a work item
|
|||
|
/// </summary>
|
|||
|
/// <param name="callback">A callback to execute</param>
|
|||
|
/// <param name="state">
|
|||
|
/// The context object of the work item. Used for passing arguments to the work item.
|
|||
|
/// </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>
|
|||
|
/// <param name="workItemPriority">The work item priority</param>
|
|||
|
/// <returns>Returns a work item result</returns>
|
|||
|
public IWorkItemResult QueueWorkItem(
|
|||
|
WorkItemCallback callback,
|
|||
|
object state,
|
|||
|
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
|||
|
CallToPostExecute callToPostExecute,
|
|||
|
WorkItemPriority workItemPriority)
|
|||
|
{
|
|||
|
ValidateNotDisposed();
|
|||
|
ValidateCallback(callback);
|
|||
|
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
|
|||
|
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)
|
|||
|
{
|
|||
|
ValidateWaitForIdle();
|
|||
|
return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
|
|||
|
}
|
|||
|
|
|||
|
private void ValidateWaitForIdle()
|
|||
|
{
|
|||
|
if(_smartThreadPool == this)
|
|||
|
{
|
|||
|
throw new NotSupportedException(
|
|||
|
"WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
|
|||
|
{
|
|||
|
ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem);
|
|||
|
if ((null != workItemsGroup) &&
|
|||
|
(null != SmartThreadPool._currentWorkItem) &&
|
|||
|
SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup))
|
|||
|
{
|
|||
|
throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|||
|
private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
|
|||
|
{
|
|||
|
if ((null != workItemsGroup) &&
|
|||
|
(null != workItem) &&
|
|||
|
workItem.WasQueuedBy(workItemsGroup))
|
|||
|
{
|
|||
|
throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <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();
|
|||
|
|
|||
|
ISTPInstancePerformanceCounters pcs = _pcs;
|
|||
|
|
|||
|
if (NullSTPInstancePerformanceCounters.Instance != _pcs)
|
|||
|
{
|
|||
|
_pcs.Dispose();
|
|||
|
// Set the _pcs to "null" to stop updating the performance
|
|||
|
// counters
|
|||
|
_pcs = NullSTPInstancePerformanceCounters.Instance;
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
// Wait for the thread to terminate
|
|||
|
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.GetHashCode();
|
|||
|
}
|
|||
|
catch(ThreadStateException ex)
|
|||
|
{
|
|||
|
ex.GetHashCode();
|
|||
|
// In case the thread has been terminated
|
|||
|
// after the check if it is alive.
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Dispose of the performance counters
|
|||
|
pcs.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
/// <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);
|
|||
|
}
|
|||
|
|
|||
|
public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
|
|||
|
{
|
|||
|
IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
|
|||
|
return workItemsGroup;
|
|||
|
}
|
|||
|
|
|||
|
public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
|
|||
|
{
|
|||
|
IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
|
|||
|
return workItemsGroup;
|
|||
|
}
|
|||
|
|
|||
|
public event WorkItemsGroupIdleHandler OnIdle
|
|||
|
{
|
|||
|
add
|
|||
|
{
|
|||
|
throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
|
|||
|
//_onIdle += value;
|
|||
|
}
|
|||
|
remove
|
|||
|
{
|
|||
|
throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
|
|||
|
//_onIdle -= value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Cancel()
|
|||
|
{
|
|||
|
ICollection workItemsGroups = _workItemsGroups.Values;
|
|||
|
foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
|
|||
|
{
|
|||
|
workItemsGroup.Cancel();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Start()
|
|||
|
{
|
|||
|
lock (this)
|
|||
|
{
|
|||
|
if (!this._stpStartInfo.StartSuspended)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
_stpStartInfo.StartSuspended = false;
|
|||
|
}
|
|||
|
|
|||
|
ICollection workItemsGroups = _workItemsGroups.Values;
|
|||
|
foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
|
|||
|
{
|
|||
|
workItemsGroup.OnSTPIsStarting();
|
|||
|
}
|
|||
|
|
|||
|
StartOptimalNumberOfThreads();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Properties
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get/Set the name of the SmartThreadPool instance
|
|||
|
/// </summary>
|
|||
|
public string Name
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return _name;
|
|||
|
}
|
|||
|
|
|||
|
set
|
|||
|
{
|
|||
|
_name = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public event EventHandler Idle
|
|||
|
{
|
|||
|
add
|
|||
|
{
|
|||
|
_stpIdle += value;
|
|||
|
}
|
|||
|
|
|||
|
remove
|
|||
|
{
|
|||
|
_stpIdle -= value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#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
|
|||
|
}
|