aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty/SmartThreadPool/SmartThreadPool.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ThirdParty/SmartThreadPool/SmartThreadPool.cs')
-rw-r--r--ThirdParty/SmartThreadPool/SmartThreadPool.cs1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs
new file mode 100644
index 0000000..c21984e
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs
@@ -0,0 +1,1438 @@
1// Ami Bar
2// amibar@gmail.com
3//
4// Smart thread pool in C#.
5// 7 Aug 2004 - Initial release
6// 14 Sep 2004 - Bug fixes
7// 15 Oct 2004 - Added new features
8// - Work items return result.
9// - Support waiting synchronization for multiple work items.
10// - Work items can be cancelled.
11// - Passage of the caller thread’s context to the thread in the pool.
12// - Minimal usage of WIN32 handles.
13// - Minor bug fixes.
14// 26 Dec 2004 - Changes:
15// - Removed static constructors.
16// - Added finalizers.
17// - Changed Exceptions so they are serializable.
18// - Fixed the bug in one of the SmartThreadPool constructors.
19// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters.
20// The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
21// - Added PostExecute with options on which cases to call it.
22// - Added option to dispose of the state objects.
23// - Added a WaitForIdle() method that waits until the work items queue is empty.
24// - Added an STPStartInfo class for the initialization of the thread pool.
25// - Changed exception handling so if a work item throws an exception it
26// is rethrown at GetResult(), rather then firing an UnhandledException event.
27// Note that PostExecute exception are always ignored.
28// 25 Mar 2005 - Changes:
29// - Fixed lost of work items bug
30// 3 Jul 2005: Changes.
31// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed.
32// 16 Aug 2005: Changes.
33// - Fixed bug where the InUseThreads becomes negative when canceling work items.
34//
35// 31 Jan 2006 - Changes:
36// - Added work items priority
37// - Removed support of chained delegates in callbacks and post executes (nobody really use this)
38// - Added work items groups
39// - Added work items groups idle event
40// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
41// it returns true rather then throwing an exception.
42// - Added option to start the STP and the WIG as suspended
43// - Exception behavior changed, the real exception is returned by an
44// inner exception
45// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.)
46// - Added performance counters
47// - Added priority to the threads in the pool
48//
49// 13 Feb 2006 - Changes:
50// - Added a call to the dispose of the Performance Counter so
51// their won't be a Performance Counter leak.
52// - Added exception catch in case the Performance Counters cannot
53// be created.
54
55using System;
56using System.Security;
57using System.Threading;
58using System.Collections;
59using System.Diagnostics;
60using System.Runtime.CompilerServices;
61
62using Amib.Threading.Internal;
63
64namespace Amib.Threading
65{
66 #region SmartThreadPool class
67 /// <summary>
68 /// Smart thread pool class.
69 /// </summary>
70 public class SmartThreadPool : IWorkItemsGroup, IDisposable
71 {
72 #region Default Constants
73
74 /// <summary>
75 /// Default minimum number of threads the thread pool contains. (0)
76 /// </summary>
77 public const int DefaultMinWorkerThreads = 0;
78
79 /// <summary>
80 /// Default maximum number of threads the thread pool contains. (25)
81 /// </summary>
82 public const int DefaultMaxWorkerThreads = 25;
83
84 /// <summary>
85 /// Default idle timeout in milliseconds. (One minute)
86 /// </summary>
87 public const int DefaultIdleTimeout = 60*1000; // One minute
88
89 /// <summary>
90 /// Indicate to copy the security context of the caller and then use it in the call. (false)
91 /// </summary>
92 public const bool DefaultUseCallerCallContext = false;
93
94 /// <summary>
95 /// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
96 /// </summary>
97 public const bool DefaultUseCallerHttpContext = false;
98
99 /// <summary>
100 /// Indicate to dispose of the state objects if they support the IDispose interface. (false)
101 /// </summary>
102 public const bool DefaultDisposeOfStateObjects = false;
103
104 /// <summary>
105 /// The default option to run the post execute
106 /// </summary>
107 public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;
108
109 /// <summary>
110 /// The default post execute method to run.
111 /// When null it means not to call it.
112 /// </summary>
113 public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null;
114
115 /// <summary>
116 /// The default work item priority
117 /// </summary>
118 public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
119
120 /// <summary>
121 /// The default is to work on work items as soon as they arrive
122 /// and not to wait for the start.
123 /// </summary>
124 public const bool DefaultStartSuspended = false;
125
126 /// <summary>
127 /// The default is not to use the performance counters
128 /// </summary>
129 public static readonly string DefaultPerformanceCounterInstanceName = null;
130
131 public static readonly int DefaultStackSize = 0;
132
133 /// <summary>
134 /// The default thread priority
135 /// </summary>
136 public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
137
138 #endregion
139
140 #region Member Variables
141
142 /// <summary>
143 /// Contains the name of this instance of SmartThreadPool.
144 /// Can be changed by the user.
145 /// </summary>
146 private string _name = "SmartThreadPool";
147
148 /// <summary>
149 /// Hashtable of all the threads in the thread pool.
150 /// </summary>
151 private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable());
152
153 /// <summary>
154 /// Queue of work items.
155 /// </summary>
156 private WorkItemsQueue _workItemsQueue = new WorkItemsQueue();
157
158 /// <summary>
159 /// Count the work items handled.
160 /// Used by the performance counter.
161 /// </summary>
162 private long _workItemsProcessed = 0;
163
164 /// <summary>
165 /// Number of threads that currently work (not idle).
166 /// </summary>
167 private int _inUseWorkerThreads = 0;
168
169 /// <summary>
170 /// Start information to use.
171 /// It is simpler than providing many constructors.
172 /// </summary>
173 private STPStartInfo _stpStartInfo = new STPStartInfo();
174
175 /// <summary>
176 /// Total number of work items that are stored in the work items queue
177 /// plus the work items that the threads in the pool are working on.
178 /// </summary>
179 private int _currentWorkItemsCount = 0;
180
181 /// <summary>
182 /// Signaled when the thread pool is idle, i.e. no thread is busy
183 /// and the work items queue is empty
184 /// </summary>
185 private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
186
187 /// <summary>
188 /// An event to signal all the threads to quit immediately.
189 /// </summary>
190 private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
191
192 /// <summary>
193 /// A flag to indicate the threads to quit.
194 /// </summary>
195 private bool _shutdown = false;
196
197 /// <summary>
198 /// Counts the threads created in the pool.
199 /// It is used to name the threads.
200 /// </summary>
201 private int _threadCounter = 0;
202
203 /// <summary>
204 /// Indicate that the SmartThreadPool has been disposed
205 /// </summary>
206 private bool _isDisposed = false;
207
208 /// <summary>
209 /// Event to send that the thread pool is idle
210 /// </summary>
211 private event EventHandler _stpIdle;
212
213 /// <summary>
214 /// On idle event
215 /// </summary>
216 //private event WorkItemsGroupIdleHandler _onIdle;
217
218 /// <summary>
219 /// Holds all the WorkItemsGroup instaces that have at least one
220 /// work item int the SmartThreadPool
221 /// This variable is used in case of Shutdown
222 /// </summary>
223 private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable());
224
225 /// <summary>
226 /// A reference from each thread in the thread pool to its SmartThreadPool
227 /// object container.
228 /// With this variable a thread can know whatever it belongs to a
229 /// SmartThreadPool.
230 /// </summary>
231 [ThreadStatic]
232 private static SmartThreadPool _smartThreadPool;
233
234 /// <summary>
235 /// A reference to the current work item a thread from the thread pool
236 /// is executing.
237 /// </summary>
238 [ThreadStatic]
239 private static WorkItem _currentWorkItem;
240
241 /// <summary>
242 /// STP performance counters
243 /// </summary>
244 private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance;
245
246 #endregion
247
248 #region Construction and Finalization
249
250 /// <summary>
251 /// Constructor
252 /// </summary>
253 public SmartThreadPool()
254 {
255 Initialize();
256 }
257
258 /// <summary>
259 /// Constructor
260 /// </summary>
261 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
262 public SmartThreadPool(int idleTimeout)
263 {
264 _stpStartInfo.IdleTimeout = idleTimeout;
265 Initialize();
266 }
267
268 /// <summary>
269 /// Constructor
270 /// </summary>
271 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
272 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
273 public SmartThreadPool(
274 int idleTimeout,
275 int maxWorkerThreads)
276 {
277 _stpStartInfo.IdleTimeout = idleTimeout;
278 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
279 Initialize();
280 }
281
282 /// <summary>
283 /// Constructor
284 /// </summary>
285 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
286 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
287 /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
288 public SmartThreadPool(
289 int idleTimeout,
290 int maxWorkerThreads,
291 int minWorkerThreads)
292 {
293 _stpStartInfo.IdleTimeout = idleTimeout;
294 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
295 _stpStartInfo.MinWorkerThreads = minWorkerThreads;
296 Initialize();
297 }
298
299 /// <summary>
300 /// Constructor
301 /// </summary>
302 public SmartThreadPool(STPStartInfo stpStartInfo)
303 {
304 _stpStartInfo = new STPStartInfo(stpStartInfo);
305 Initialize();
306 }
307
308 private void Initialize()
309 {
310 ValidateSTPStartInfo();
311
312 if (null != _stpStartInfo.PerformanceCounterInstanceName)
313 {
314 try
315 {
316 _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
317 }
318 catch(Exception e)
319 {
320 Debug.WriteLine("Unable to create Performance Counters: " + e.ToString());
321 _pcs = NullSTPInstancePerformanceCounters.Instance;
322 }
323 }
324
325 StartOptimalNumberOfThreads();
326 }
327
328 private void StartOptimalNumberOfThreads()
329 {
330 int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
331 threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
332 StartThreads(threadsCount);
333 }
334
335 private void ValidateSTPStartInfo()
336 {
337 if (_stpStartInfo.MinWorkerThreads < 0)
338 {
339 throw new ArgumentOutOfRangeException(
340 "MinWorkerThreads", "MinWorkerThreads cannot be negative");
341 }
342
343 if (_stpStartInfo.MaxWorkerThreads <= 0)
344 {
345 throw new ArgumentOutOfRangeException(
346 "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
347 }
348
349 if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
350 {
351 throw new ArgumentOutOfRangeException(
352 "MinWorkerThreads, maxWorkerThreads",
353 "MaxWorkerThreads must be greater or equal to MinWorkerThreads");
354 }
355 }
356
357 private void ValidateCallback(Delegate callback)
358 {
359 if(callback.GetInvocationList().Length > 1)
360 {
361 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
362 }
363 }
364
365 #endregion
366
367 #region Thread Processing
368
369 /// <summary>
370 /// Waits on the queue for a work item, shutdown, or timeout.
371 /// </summary>
372 /// <returns>
373 /// Returns the WaitingCallback or null in case of timeout or shutdown.
374 /// </returns>
375 private WorkItem Dequeue()
376 {
377 WorkItem workItem =
378 _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent);
379
380 return workItem;
381 }
382
383 /// <summary>
384 /// Put a new work item in the queue
385 /// </summary>
386 /// <param name="workItem">A work item to queue</param>
387 private void Enqueue(WorkItem workItem)
388 {
389 Enqueue(workItem, true);
390 }
391
392 /// <summary>
393 /// Put a new work item in the queue
394 /// </summary>
395 /// <param name="workItem">A work item to queue</param>
396 internal void Enqueue(WorkItem workItem, bool incrementWorkItems)
397 {
398 // Make sure the workItem is not null
399 Debug.Assert(null != workItem);
400
401 if (incrementWorkItems)
402 {
403 IncrementWorkItemsCount();
404 }
405
406 _workItemsQueue.EnqueueWorkItem(workItem);
407 workItem.WorkItemIsQueued();
408
409 // If all the threads are busy then try to create a new one
410 if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count)
411 {
412 StartThreads(1);
413 }
414 }
415
416 private void IncrementWorkItemsCount()
417 {
418 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
419
420 int count = Interlocked.Increment(ref _currentWorkItemsCount);
421 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
422 if (count == 1)
423 {
424 //Trace.WriteLine("STP is NOT idle");
425 _isIdleWaitHandle.Reset();
426 }
427 }
428
429 private void DecrementWorkItemsCount()
430 {
431 ++_workItemsProcessed;
432
433 // The counter counts even if the work item was cancelled
434 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
435
436 int count = Interlocked.Decrement(ref _currentWorkItemsCount);
437 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
438 if (count == 0)
439 {
440 //Trace.WriteLine("STP is idle");
441 _isIdleWaitHandle.Set();
442 }
443 }
444
445 internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
446 {
447 _workItemsGroups[workItemsGroup] = workItemsGroup;
448 }
449
450 internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
451 {
452 if (_workItemsGroups.Contains(workItemsGroup))
453 {
454 _workItemsGroups.Remove(workItemsGroup);
455 }
456 }
457
458 /// <summary>
459 /// Inform that the current thread is about to quit or quiting.
460 /// The same thread may call this method more than once.
461 /// </summary>
462 private void InformCompleted()
463 {
464 // There is no need to lock the two methods together
465 // since only the current thread removes itself
466 // and the _workerThreads is a synchronized hashtable
467 if (_workerThreads.Contains(Thread.CurrentThread))
468 {
469 _workerThreads.Remove(Thread.CurrentThread);
470 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
471 }
472 }
473
474 /// <summary>
475 /// Starts new threads
476 /// </summary>
477 /// <param name="threadsCount">The number of threads to start</param>
478 private void StartThreads(int threadsCount)
479 {
480 if (_stpStartInfo.StartSuspended)
481 {
482 return;
483 }
484
485 lock(_workerThreads.SyncRoot)
486 {
487 // Don't start threads on shut down
488 if (_shutdown)
489 {
490 return;
491 }
492
493 for(int i = 0; i < threadsCount; ++i)
494 {
495 // Don't create more threads then the upper limit
496 if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
497 {
498 return;
499 }
500
501 // Create a new thread
502 Thread workerThread = new Thread(new ThreadStart(ProcessQueuedItems), _stpStartInfo.StackSize);
503
504 // Configure the new thread and start it
505 workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
506 workerThread.IsBackground = true;
507 workerThread.Priority = _stpStartInfo.ThreadPriority;
508 workerThread.Start();
509 ++_threadCounter;
510
511 // Add the new thread to the hashtable and update its creation
512 // time.
513 _workerThreads[workerThread] = DateTime.Now;
514 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
515 }
516 }
517 }
518
519 /// <summary>
520 /// A worker thread method that processes work items from the work items queue.
521 /// </summary>
522 private void ProcessQueuedItems()
523 {
524 // Initialize the _smartThreadPool variable
525 _smartThreadPool = this;
526
527 try
528 {
529 bool bInUseWorkerThreadsWasIncremented = false;
530
531 // Process until shutdown.
532 while(!_shutdown)
533 {
534 // Update the last time this thread was seen alive.
535 // It's good for debugging.
536 _workerThreads[Thread.CurrentThread] = DateTime.Now;
537
538 // Wait for a work item, shutdown, or timeout
539 WorkItem workItem = Dequeue();
540
541 // Update the last time this thread was seen alive.
542 // It's good for debugging.
543 _workerThreads[Thread.CurrentThread] = DateTime.Now;
544
545 // On timeout or shut down.
546 if (null == workItem)
547 {
548 // Double lock for quit.
549 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
550 {
551 lock(_workerThreads.SyncRoot)
552 {
553 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
554 {
555 // Inform that the thread is quiting and then quit.
556 // This method must be called within this lock or else
557 // more threads will quit and the thread pool will go
558 // below the lower limit.
559 InformCompleted();
560 break;
561 }
562 }
563 }
564 }
565
566 // If we didn't quit then skip to the next iteration.
567 if (null == workItem)
568 {
569 continue;
570 }
571
572 try
573 {
574 // Initialize the value to false
575 bInUseWorkerThreadsWasIncremented = false;
576
577 // Change the state of the work item to 'in progress' if possible.
578 // We do it here so if the work item has been canceled we won't
579 // increment the _inUseWorkerThreads.
580 // The cancel mechanism doesn't delete items from the queue,
581 // it marks the work item as canceled, and when the work item
582 // is dequeued, we just skip it.
583 // If the post execute of work item is set to always or to
584 // call when the work item is canceled then the StartingWorkItem()
585 // will return true, so the post execute can run.
586 if (!workItem.StartingWorkItem())
587 {
588 continue;
589 }
590
591 // Execute the callback. Make sure to accurately
592 // record how many callbacks are currently executing.
593 int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads);
594 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
595
596 // Mark that the _inUseWorkerThreads incremented, so in the finally{}
597 // statement we will decrement it correctly.
598 bInUseWorkerThreadsWasIncremented = true;
599
600 // Set the _currentWorkItem to the current work item
601 _currentWorkItem = workItem;
602
603 lock(workItem)
604 {
605 workItem.currentThread = Thread.CurrentThread;
606 }
607
608 ExecuteWorkItem(workItem);
609
610 lock(workItem)
611 {
612 workItem.currentThread = null;
613 }
614
615 }
616 catch(ThreadAbortException ex)
617 {
618 lock(workItem)
619 {
620 workItem.currentThread = null;
621 }
622 ex.GetHashCode();
623 Thread.ResetAbort();
624 }
625 catch(Exception ex)
626 {
627 ex.GetHashCode();
628 // Do nothing
629 }
630 finally
631 {
632 lock(workItem)
633 {
634 workItem.currentThread = null;
635 }
636
637 if (null != workItem)
638 {
639 workItem.DisposeOfState();
640 }
641
642 // Set the _currentWorkItem to null, since we
643 // no longer run user's code.
644 _currentWorkItem = null;
645
646 // Decrement the _inUseWorkerThreads only if we had
647 // incremented it. Note the cancelled work items don't
648 // increment _inUseWorkerThreads.
649 if (bInUseWorkerThreadsWasIncremented)
650 {
651 int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads);
652 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
653 }
654
655 // Notify that the work item has been completed.
656 // WorkItemsGroup may enqueue their next work item.
657 workItem.FireWorkItemCompleted();
658
659 // Decrement the number of work items here so the idle
660 // ManualResetEvent won't fluctuate.
661 DecrementWorkItemsCount();
662 }
663 }
664 }
665 catch(ThreadAbortException tae)
666 {
667 tae.GetHashCode();
668 // Handle the abort exception gracfully.
669 Thread.ResetAbort();
670 }
671 catch(Exception e)
672 {
673 Debug.Assert(null != e);
674 }
675 finally
676 {
677 InformCompleted();
678 }
679 }
680
681 private void ExecuteWorkItem(WorkItem workItem)
682 {
683 _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime);
684 try
685 {
686 workItem.Execute();
687 }
688 catch
689 {
690 throw;
691 }
692 finally
693 {
694 _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime);
695 }
696 }
697
698
699 #endregion
700
701 #region Public Methods
702
703 /// <summary>
704 /// Queue a work item
705 /// </summary>
706 /// <param name="callback">A callback to execute</param>
707 /// <returns>Returns a work item result</returns>
708 public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
709 {
710 ValidateNotDisposed();
711 ValidateCallback(callback);
712 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback);
713 Enqueue(workItem);
714 return workItem.GetWorkItemResult();
715 }
716
717 /// <summary>
718 /// Queue a work item
719 /// </summary>
720 /// <param name="callback">A callback to execute</param>
721 /// <param name="workItemPriority">The priority of the work item</param>
722 /// <returns>Returns a work item result</returns>
723 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
724 {
725 ValidateNotDisposed();
726 ValidateCallback(callback);
727 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority);
728 Enqueue(workItem);
729 return workItem.GetWorkItemResult();
730 }
731
732 /// <summary>
733 /// Queue a work item
734 /// </summary>
735 /// <param name="workItemInfo">Work item info</param>
736 /// <param name="callback">A callback to execute</param>
737 /// <returns>Returns a work item result</returns>
738 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
739 {
740 ValidateNotDisposed();
741 ValidateCallback(callback);
742 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback);
743 Enqueue(workItem);
744 return workItem.GetWorkItemResult();
745 }
746
747 /// <summary>
748 /// Queue a work item
749 /// </summary>
750 /// <param name="callback">A callback to execute</param>
751 /// <param name="state">
752 /// The context object of the work item. Used for passing arguments to the work item.
753 /// </param>
754 /// <returns>Returns a work item result</returns>
755 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
756 {
757 ValidateNotDisposed();
758 ValidateCallback(callback);
759 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state);
760 Enqueue(workItem);
761 return workItem.GetWorkItemResult();
762 }
763
764 /// <summary>
765 /// Queue a work item
766 /// </summary>
767 /// <param name="callback">A callback to execute</param>
768 /// <param name="state">
769 /// The context object of the work item. Used for passing arguments to the work item.
770 /// </param>
771 /// <param name="workItemPriority">The work item priority</param>
772 /// <returns>Returns a work item result</returns>
773 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
774 {
775 ValidateNotDisposed();
776 ValidateCallback(callback);
777 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority);
778 Enqueue(workItem);
779 return workItem.GetWorkItemResult();
780 }
781
782 /// <summary>
783 /// Queue a work item
784 /// </summary>
785 /// <param name="workItemInfo">Work item information</param>
786 /// <param name="callback">A callback to execute</param>
787 /// <param name="state">
788 /// The context object of the work item. Used for passing arguments to the work item.
789 /// </param>
790 /// <returns>Returns a work item result</returns>
791 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
792 {
793 ValidateNotDisposed();
794 ValidateCallback(callback);
795 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state);
796 Enqueue(workItem);
797 return workItem.GetWorkItemResult();
798 }
799
800 /// <summary>
801 /// Queue a work item
802 /// </summary>
803 /// <param name="callback">A callback to execute</param>
804 /// <param name="state">
805 /// The context object of the work item. Used for passing arguments to the work item.
806 /// </param>
807 /// <param name="postExecuteWorkItemCallback">
808 /// A delegate to call after the callback completion
809 /// </param>
810 /// <returns>Returns a work item result</returns>
811 public IWorkItemResult QueueWorkItem(
812 WorkItemCallback callback,
813 object state,
814 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
815 {
816 ValidateNotDisposed();
817 ValidateCallback(callback);
818 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback);
819 Enqueue(workItem);
820 return workItem.GetWorkItemResult();
821 }
822
823 /// <summary>
824 /// Queue a work item
825 /// </summary>
826 /// <param name="callback">A callback to execute</param>
827 /// <param name="state">
828 /// The context object of the work item. Used for passing arguments to the work item.
829 /// </param>
830 /// <param name="postExecuteWorkItemCallback">
831 /// A delegate to call after the callback completion
832 /// </param>
833 /// <param name="workItemPriority">The work item priority</param>
834 /// <returns>Returns a work item result</returns>
835 public IWorkItemResult QueueWorkItem(
836 WorkItemCallback callback,
837 object state,
838 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
839 WorkItemPriority workItemPriority)
840 {
841 ValidateNotDisposed();
842 ValidateCallback(callback);
843 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
844 Enqueue(workItem);
845 return workItem.GetWorkItemResult();
846 }
847
848 /// <summary>
849 /// Queue a work item
850 /// </summary>
851 /// <param name="callback">A callback to execute</param>
852 /// <param name="state">
853 /// The context object of the work item. Used for passing arguments to the work item.
854 /// </param>
855 /// <param name="postExecuteWorkItemCallback">
856 /// A delegate to call after the callback completion
857 /// </param>
858 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
859 /// <returns>Returns a work item result</returns>
860 public IWorkItemResult QueueWorkItem(
861 WorkItemCallback callback,
862 object state,
863 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
864 CallToPostExecute callToPostExecute)
865 {
866 ValidateNotDisposed();
867 ValidateCallback(callback);
868 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
869 Enqueue(workItem);
870 return workItem.GetWorkItemResult();
871 }
872
873 /// <summary>
874 /// Queue a work item
875 /// </summary>
876 /// <param name="callback">A callback to execute</param>
877 /// <param name="state">
878 /// The context object of the work item. Used for passing arguments to the work item.
879 /// </param>
880 /// <param name="postExecuteWorkItemCallback">
881 /// A delegate to call after the callback completion
882 /// </param>
883 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
884 /// <param name="workItemPriority">The work item priority</param>
885 /// <returns>Returns a work item result</returns>
886 public IWorkItemResult QueueWorkItem(
887 WorkItemCallback callback,
888 object state,
889 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
890 CallToPostExecute callToPostExecute,
891 WorkItemPriority workItemPriority)
892 {
893 ValidateNotDisposed();
894 ValidateCallback(callback);
895 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
896 Enqueue(workItem);
897 return workItem.GetWorkItemResult();
898 }
899
900 /// <summary>
901 /// Wait for the thread pool to be idle
902 /// </summary>
903 public void WaitForIdle()
904 {
905 WaitForIdle(Timeout.Infinite);
906 }
907
908 /// <summary>
909 /// Wait for the thread pool to be idle
910 /// </summary>
911 public bool WaitForIdle(TimeSpan timeout)
912 {
913 return WaitForIdle((int)timeout.TotalMilliseconds);
914 }
915
916 /// <summary>
917 /// Wait for the thread pool to be idle
918 /// </summary>
919 public bool WaitForIdle(int millisecondsTimeout)
920 {
921 ValidateWaitForIdle();
922 return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
923 }
924
925 private void ValidateWaitForIdle()
926 {
927 if(_smartThreadPool == this)
928 {
929 throw new NotSupportedException(
930 "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
931 }
932 }
933
934 internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
935 {
936 ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem);
937 if ((null != workItemsGroup) &&
938 (null != SmartThreadPool._currentWorkItem) &&
939 SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup))
940 {
941 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
942 }
943 }
944
945 [MethodImpl(MethodImplOptions.NoInlining)]
946 private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
947 {
948 if ((null != workItemsGroup) &&
949 (null != workItem) &&
950 workItem.WasQueuedBy(workItemsGroup))
951 {
952 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
953 }
954 }
955
956
957
958 /// <summary>
959 /// Force the SmartThreadPool to shutdown
960 /// </summary>
961 public void Shutdown()
962 {
963 Shutdown(true, 0);
964 }
965
966 public void Shutdown(bool forceAbort, TimeSpan timeout)
967 {
968 Shutdown(forceAbort, (int)timeout.TotalMilliseconds);
969 }
970
971 /// <summary>
972 /// Empties the queue of work items and abort the threads in the pool.
973 /// </summary>
974 public void Shutdown(bool forceAbort, int millisecondsTimeout)
975 {
976 ValidateNotDisposed();
977
978 ISTPInstancePerformanceCounters pcs = _pcs;
979
980 if (NullSTPInstancePerformanceCounters.Instance != _pcs)
981 {
982 _pcs.Dispose();
983 // Set the _pcs to "null" to stop updating the performance
984 // counters
985 _pcs = NullSTPInstancePerformanceCounters.Instance;
986 }
987
988 Thread [] threads = null;
989 lock(_workerThreads.SyncRoot)
990 {
991 // Shutdown the work items queue
992 _workItemsQueue.Dispose();
993
994 // Signal the threads to exit
995 _shutdown = true;
996 _shuttingDownEvent.Set();
997
998 // Make a copy of the threads' references in the pool
999 threads = new Thread [_workerThreads.Count];
1000 _workerThreads.Keys.CopyTo(threads, 0);
1001 }
1002
1003 int millisecondsLeft = millisecondsTimeout;
1004 DateTime start = DateTime.Now;
1005 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
1006 bool timeout = false;
1007
1008 // Each iteration we update the time left for the timeout.
1009 foreach(Thread thread in threads)
1010 {
1011 // Join don't work with negative numbers
1012 if (!waitInfinitely && (millisecondsLeft < 0))
1013 {
1014 timeout = true;
1015 break;
1016 }
1017
1018 // Wait for the thread to terminate
1019 bool success = thread.Join(millisecondsLeft);
1020 if(!success)
1021 {
1022 timeout = true;
1023 break;
1024 }
1025
1026 if(!waitInfinitely)
1027 {
1028 // Update the time left to wait
1029 TimeSpan ts = DateTime.Now - start;
1030 millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
1031 }
1032 }
1033
1034 if (timeout && forceAbort)
1035 {
1036 // Abort the threads in the pool
1037 foreach(Thread thread in threads)
1038 {
1039 if ((thread != null) && thread.IsAlive)
1040 {
1041 try
1042 {
1043 thread.Abort("Shutdown");
1044 }
1045 catch(SecurityException e)
1046 {
1047 e.GetHashCode();
1048 }
1049 catch(ThreadStateException ex)
1050 {
1051 ex.GetHashCode();
1052 // In case the thread has been terminated
1053 // after the check if it is alive.
1054 }
1055 }
1056 }
1057 }
1058
1059 // Dispose of the performance counters
1060 pcs.Dispose();
1061 }
1062
1063 /// <summary>
1064 /// Wait for all work items to complete
1065 /// </summary>
1066 /// <param name="workItemResults">Array of work item result objects</param>
1067 /// <returns>
1068 /// true when every work item in workItemResults has completed; otherwise false.
1069 /// </returns>
1070 public static bool WaitAll(
1071 IWorkItemResult [] workItemResults)
1072 {
1073 return WaitAll(workItemResults, Timeout.Infinite, true);
1074 }
1075
1076 /// <summary>
1077 /// Wait for all work items to complete
1078 /// </summary>
1079 /// <param name="workItemResults">Array of work item result objects</param>
1080 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1081 /// <param name="exitContext">
1082 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1083 /// </param>
1084 /// <returns>
1085 /// true when every work item in workItemResults has completed; otherwise false.
1086 /// </returns>
1087 public static bool WaitAll(
1088 IWorkItemResult [] workItemResults,
1089 TimeSpan timeout,
1090 bool exitContext)
1091 {
1092 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
1093 }
1094
1095 /// <summary>
1096 /// Wait for all work items to complete
1097 /// </summary>
1098 /// <param name="workItemResults">Array of work item result objects</param>
1099 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1100 /// <param name="exitContext">
1101 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1102 /// </param>
1103 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1104 /// <returns>
1105 /// true when every work item in workItemResults has completed; otherwise false.
1106 /// </returns>
1107 public static bool WaitAll(
1108 IWorkItemResult [] workItemResults,
1109 TimeSpan timeout,
1110 bool exitContext,
1111 WaitHandle cancelWaitHandle)
1112 {
1113 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1114 }
1115
1116 /// <summary>
1117 /// Wait for all work items to complete
1118 /// </summary>
1119 /// <param name="workItemResults">Array of work item result objects</param>
1120 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1121 /// <param name="exitContext">
1122 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1123 /// </param>
1124 /// <returns>
1125 /// true when every work item in workItemResults has completed; otherwise false.
1126 /// </returns>
1127 public static bool WaitAll(
1128 IWorkItemResult [] workItemResults,
1129 int millisecondsTimeout,
1130 bool exitContext)
1131 {
1132 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null);
1133 }
1134
1135 /// <summary>
1136 /// Wait for all work items to complete
1137 /// </summary>
1138 /// <param name="workItemResults">Array of work item result objects</param>
1139 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1140 /// <param name="exitContext">
1141 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1142 /// </param>
1143 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1144 /// <returns>
1145 /// true when every work item in workItemResults has completed; otherwise false.
1146 /// </returns>
1147 public static bool WaitAll(
1148 IWorkItemResult [] workItemResults,
1149 int millisecondsTimeout,
1150 bool exitContext,
1151 WaitHandle cancelWaitHandle)
1152 {
1153 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1154 }
1155
1156
1157 /// <summary>
1158 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1159 /// </summary>
1160 /// <param name="workItemResults">Array of work item result objects</param>
1161 /// <returns>
1162 /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
1163 /// </returns>
1164 public static int WaitAny(
1165 IWorkItemResult [] workItemResults)
1166 {
1167 return WaitAny(workItemResults, Timeout.Infinite, true);
1168 }
1169
1170 /// <summary>
1171 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1172 /// </summary>
1173 /// <param name="workItemResults">Array of work item result objects</param>
1174 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1175 /// <param name="exitContext">
1176 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1177 /// </param>
1178 /// <returns>
1179 /// 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.
1180 /// </returns>
1181 public static int WaitAny(
1182 IWorkItemResult [] workItemResults,
1183 TimeSpan timeout,
1184 bool exitContext)
1185 {
1186 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
1187 }
1188
1189 /// <summary>
1190 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1191 /// </summary>
1192 /// <param name="workItemResults">Array of work item result objects</param>
1193 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1194 /// <param name="exitContext">
1195 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1196 /// </param>
1197 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1198 /// <returns>
1199 /// 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.
1200 /// </returns>
1201 public static int WaitAny(
1202 IWorkItemResult [] workItemResults,
1203 TimeSpan timeout,
1204 bool exitContext,
1205 WaitHandle cancelWaitHandle)
1206 {
1207 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1208 }
1209
1210 /// <summary>
1211 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1212 /// </summary>
1213 /// <param name="workItemResults">Array of work item result objects</param>
1214 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1215 /// <param name="exitContext">
1216 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1217 /// </param>
1218 /// <returns>
1219 /// 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.
1220 /// </returns>
1221 public static int WaitAny(
1222 IWorkItemResult [] workItemResults,
1223 int millisecondsTimeout,
1224 bool exitContext)
1225 {
1226 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null);
1227 }
1228
1229 /// <summary>
1230 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1231 /// </summary>
1232 /// <param name="workItemResults">Array of work item result objects</param>
1233 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1234 /// <param name="exitContext">
1235 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1236 /// </param>
1237 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1238 /// <returns>
1239 /// 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.
1240 /// </returns>
1241 public static int WaitAny(
1242 IWorkItemResult [] workItemResults,
1243 int millisecondsTimeout,
1244 bool exitContext,
1245 WaitHandle cancelWaitHandle)
1246 {
1247 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1248 }
1249
1250 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
1251 {
1252 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
1253 return workItemsGroup;
1254 }
1255
1256 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
1257 {
1258 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
1259 return workItemsGroup;
1260 }
1261
1262 public event WorkItemsGroupIdleHandler OnIdle
1263 {
1264 add
1265 {
1266 throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
1267 //_onIdle += value;
1268 }
1269 remove
1270 {
1271 throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
1272 //_onIdle -= value;
1273 }
1274 }
1275
1276 public void Cancel()
1277 {
1278 ICollection workItemsGroups = _workItemsGroups.Values;
1279 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1280 {
1281 workItemsGroup.Cancel();
1282 }
1283 }
1284
1285 public void Start()
1286 {
1287 lock (this)
1288 {
1289 if (!this._stpStartInfo.StartSuspended)
1290 {
1291 return;
1292 }
1293 _stpStartInfo.StartSuspended = false;
1294 }
1295
1296 ICollection workItemsGroups = _workItemsGroups.Values;
1297 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1298 {
1299 workItemsGroup.OnSTPIsStarting();
1300 }
1301
1302 StartOptimalNumberOfThreads();
1303 }
1304
1305 #endregion
1306
1307 #region Properties
1308
1309 /// <summary>
1310 /// Get/Set the name of the SmartThreadPool instance
1311 /// </summary>
1312 public string Name
1313 {
1314 get
1315 {
1316 return _name;
1317 }
1318
1319 set
1320 {
1321 _name = value;
1322 }
1323 }
1324
1325 /// <summary>
1326 /// Get the lower limit of threads in the pool.
1327 /// </summary>
1328 public int MinThreads
1329 {
1330 get
1331 {
1332 ValidateNotDisposed();
1333 return _stpStartInfo.MinWorkerThreads;
1334 }
1335 }
1336
1337 /// <summary>
1338 /// Get the upper limit of threads in the pool.
1339 /// </summary>
1340 public int MaxThreads
1341 {
1342 get
1343 {
1344 ValidateNotDisposed();
1345 return _stpStartInfo.MaxWorkerThreads;
1346 }
1347 }
1348 /// <summary>
1349 /// Get the number of threads in the thread pool.
1350 /// Should be between the lower and the upper limits.
1351 /// </summary>
1352 public int ActiveThreads
1353 {
1354 get
1355 {
1356 ValidateNotDisposed();
1357 return _workerThreads.Count;
1358 }
1359 }
1360
1361 /// <summary>
1362 /// Get the number of busy (not idle) threads in the thread pool.
1363 /// </summary>
1364 public int InUseThreads
1365 {
1366 get
1367 {
1368 ValidateNotDisposed();
1369 return _inUseWorkerThreads;
1370 }
1371 }
1372
1373 /// <summary>
1374 /// Get the number of work items in the queue.
1375 /// </summary>
1376 public int WaitingCallbacks
1377 {
1378 get
1379 {
1380 ValidateNotDisposed();
1381 return _workItemsQueue.Count;
1382 }
1383 }
1384
1385
1386 public event EventHandler Idle
1387 {
1388 add
1389 {
1390 _stpIdle += value;
1391 }
1392
1393 remove
1394 {
1395 _stpIdle -= value;
1396 }
1397 }
1398
1399 #endregion
1400
1401 #region IDisposable Members
1402
1403// ~SmartThreadPool()
1404// {
1405// Dispose();
1406// }
1407
1408 public void Dispose()
1409 {
1410 if (!_isDisposed)
1411 {
1412 if (!_shutdown)
1413 {
1414 Shutdown();
1415 }
1416
1417 if (null != _shuttingDownEvent)
1418 {
1419 _shuttingDownEvent.Close();
1420 _shuttingDownEvent = null;
1421 }
1422 _workerThreads.Clear();
1423 _isDisposed = true;
1424 GC.SuppressFinalize(this);
1425 }
1426 }
1427
1428 private void ValidateNotDisposed()
1429 {
1430 if(_isDisposed)
1431 {
1432 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
1433 }
1434 }
1435 #endregion
1436 }
1437 #endregion
1438}