diff --git a/Docs/SmartThreadPool.html b/Docs/SmartThreadPool.html index 3ca2d68..dddd506 100644 --- a/Docs/SmartThreadPool.html +++ b/Docs/SmartThreadPool.html @@ -92,7 +92,7 @@
Note that in order to use WaitAny() and WaitAll(), you need to work in MTA, because internally I use WaitHandle.WaitAny() and WaitHandle.WaitAll() which requires it. If you don't do that, the methods will throw an exception to remind you.
Also note that Windows supports WaitAny() of up to 64 handles. The WaitAll() is more flexible and I re-implemented it so it is not limited to 64 handles.
See in the examples section below the code snippets for WaitAll and WaitAny.
-This feature enables to cancel work items.
@@ -502,7 +502,7 @@ Here is an example of a cooperative work item: } } -This feature should be elementary, but it is not so simple to implement. In order to pass the thread's context, the caller thread's CompressedStack should be passed. This is impossible since Microsoft blocks this option with security. Other parts of the thread's context can be passed. These include:
CurrentCulture - The culture of the thread.
@@ -518,12 +518,12 @@ Here is an example of a cooperative work item:
The seventh feature is a result of Kevin's comment on the earlier version of Smart Thread Pool. It seemed that the test application consumed a lot of handles (Handle Count in the Task Manager) without freeing them. After a few tests, I got to the conclusion that the Close() method of ManualResetEvent class doesn't always release the Win32 event handle immediately, and waits for the garbage collector to do that. Hence, running the GC explicitly releases the handles.
To make this problem less acute, I used a new approach. First, I wanted to create less number of handles, second, I wanted to reuse the handles I had already created. Therefore, I need not expose any WaitHandle but use them internally and then close them.
In order to create fewer handles, I created the ManualResetEvent objects only when the user asks for them (lazy creation). For example, if you don't use the GetResult() of the IWorkItemResult interface then a handle is not created. Using SmartThreadPool.WaitAll() and SmartThreadPool.WaitAny() creates a handle.
The work item queue created a lot of handles since each new wait for a work item created a new ManualResetEvent. Hence, a handle for each work item. The waiters of the queue are always the same threads and a thread cannot wait more than once. So now, every thread in the thread pool has its own ManualResetEvent and reuses it. To avoid coupling of the work items queue and the thread pool implementation, the work items queue stores a context inside the TLS (Thread Local Storage) of the thread.
A PostExecute is a callback method that is called right after the work item execution has been completed. It runs in the same context of the thread that executed the work item. The user can choose the cases in which the PostExecute is called. The options are represented in the CallToPostExecute flagged enumerator:
[Flags]
public enum CallToPostExecute
{
@@ -540,7 +540,7 @@ public enum CallToPostExecute
Always – Always run the PostExecute. The SmartThreadPool has a default CallToPostExecute value of CallToPostExecute.Always. This can be changed during the construction of the SmartThreadPool in the STPStartInfo class argument. Another way to give the CallToPostExecute value is in one of the SmartThreadPool.QueueWorkItem overloads. Note that as opposed to the WorkItem execution, if an exception has been thrown during the PostExecute, then it is ignored. The PostExecute is a delegate with the following signature:
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
As you can see, the PostExecute receives as an argument of type IWorkItemResult. It can be used to get the result of the work item, or any other information made available by the IWorkItemResult interface.
When the user calls the QueueWorkItem, he/she can provide a state object. The state object usually stores specific information, such as arguments, that should be used within the WorkItemCallback delegate.
The state object life time depends on its contents and the user's application. Sometimes, it is useful to dispose off the state object just after the work item has been completed. Especially if it contains unmanaged resources.
For this reason, I added a boolean to the SmartThreadPool that indicates to call Dispose on the state object when the work item has been completed. The boolean is initialized when the thread pool is constructed with the STPStartInfo. The Dispose is called only if the state object implements the IDisposable interface. The Dispose is called after the WorkItem has been completed and its PostExecute has run (if a PostExecute exists). The state object is disposed even if the work item has been canceled or the thread pool has been shutdown.
state argument that
comes with WorkItemCallback, it doesn't apply to the
arguments supplied in Action<...> and Func<...> arguments!!!
-This feature enables the user to wait for a Smart Thread Pool or a Work Items Group to become idle. They become idle when the work items queue is empty and all the @@ -563,7 +563,7 @@ public enum CallToPostExecute
The SmartThreadPool always keeps track of how many work items it has. When a new work item is queued, the number is incremented. When a thread completes a work item, the number is decremented. The total number of work items includes the work items in the queue and the work items that the threads are currently working on.
The WaitForIdle() mechanism works with a private ManualResetEvent. When a work item is queued, the ManualResetEvent is reset (changed to non signaled state). When the work items count becomes zero (initial state of the Smart Thread Pool), the ManualResetEvent is set (changed to signaled state). The WaitForIdle() method just waits for the ManualResetEvent to implement its functionality.
See the example below.
-After I did some reading about delegates and their implementation, I decided to change the way the SmartThreadPool treats exceptions. In the previous versions, I used an event driven mechanism. Entities were registered to the SmartThreadPool.UnhandledException event, and when a work item threw an exception, this event was fired. This is the behavior of Toub’s thread pool.
.NET delegates behave differently. Instead of using an event driven mechanism, it re-throws the exception of the delegated method at the EndInvoke(). Similarly, the SmartThreadPool exception mechanism is changed so that exceptions are no longer fired by the UnhandledException event, but rather re-thrown again when IWorkItemResult.GetResult() is called.
Note that exceptions slow down .NET and degrade the performance. .NET works faster when no exceptions are thrown at all. For this reason, I added an output parameter to some of the GetResult() overloads, so the exception can be retrieved rather than re-thrown. The work item throws the exception anyway, so re-throwing it will be a waste of time. As a rule of thumb, it is better to use the output parameter than catch the re-thrown exception.
Note that PostExecute is called, as and when needed, even if the work item has thrown an exception. Of course, PostExecute implementation should handle exceptions if it calls GetResult().
Also note that if the PostExecute throws an exception then its exception is ignored.
See the example below.
-Work items priority enables the user to order work items at run time. Work items are ordered by their priority. High priority is treated first. There are five priorities:
public enum WorkItemPriority
{
Lowest,
@@ -583,10 +583,10 @@ public enum CallToPostExecute
The default priority is Normal.
The implementation of priorities is quite simple. Instead of using one queue that keeps the work items sorted inside, I used one queue for each priority. Each queue is a FIFO. When the user enqueues a work item, the work item is added to the queue with a matching priority. When a thread dequeues a work item, it looks for the highest priority queue that is not empty.
This is the easiest solution to sort the work items.
-This feature improves 6, and was implemented by Steven T. I just replaced my code with that implementation.
With this feature the Smart Thread Pool can be used with ASP.NET to pass the context of HTTP between the caller thread and the thread in the pool that will execute the work item.
-This feature enables the user to execute a group of work items specifying the maximum level of concurrency.
For example, assume that your application uses several resources, the resources are not thread safe so only one thread can use a resource at a time. There are a @@ -634,7 +634,7 @@ void Print(Printer printer, Document document) {...}
To accomplish the concurrency level, the WorkItemsGroup registers to the completion event of its work items. The event is used internally and is not exposed to the user. Once registered, the WorkItemsGroup will get an event when its work item is completed. The event will trigger the WorkItemsGroup to queue more work items into the SmartThreadPool. The event is the only way to accomplish the concurrency level. When I tried to do it with PostExecute I got fluctuating WaitForIdle.
Another advantage of the WorkItemsGroup is that it can cancel all its work items that haven't been executed yet in one method with a complexity of O(1). The WorkItemsGroup does so by attaching an object to each one of its work items that indicates if the WorkItemsGroup has been cancelled. When a work item is about to be executed, it is asked for its current state (InQueue, InProgress, Completed, or Canceled). The final state considers this object's value to know if the work item was cancelled.
- The Work Items Group can also be use as a conjuction point. Say you want to accoplish + The Work Items Group can also be used as a conjuction point. Say you want to accoplish a task by splitting to a subtasks. Once the subtasks are completed a new task is issued and splitted to subtasks too. This can be achieved by using the OnIdle event of WorkItemsGroup. See conjuction point example.
@@ -643,14 +643,14 @@ void Print(Printer printer, Document document) {...}See WorkItemsGroupDemo demo in the source code solution.

When a Smart Thread Pool is created, by default, it starts its threads immediately. However, sometimes you need to queue a few work items and only then start executing them.
In these cases, you can create the Smart Thread Pool and the Work Items Group in a suspended state. When you need to execute the work items, just call the Start() method. The same method exists in the Work Items Group for the same purpose.
Note that if you create a suspended Work Items Group in a suspended Smart Thread Pool, starting the Work Items Group won't execute the work items until the Smart Thread Pool is started.
-The STPStartInfo contains a property that defines the priority in which the threads are started in the SmartThreadPool. Use it if you know what you are doing. Playing with threads priority may end up with dead locks, live lock, and days locked :-(.
MaxThreads/MinThreads/Concurrency can
+ MaxThreads/MinThreads/Concurrency can
be changed at run time.
This addition allows the user to control the concurrency of work items execution. @@ -684,10 +684,10 @@ void Print(Printer printer, Document document) {...}
The number of MaxThreads must be greater or equal to MinThreads. If MaxThreads is set to a number lower than MinThreads than MinThreads is also set to the new value of MaxThreads. And vice versa for MinThreads.
+
This functionality enables the user to execute code when a thread is created or terminated in the thread pool. The code is executed within the thread’s context. @@ -720,7 +720,7 @@ pool. The event is called from the terminated thread. In this event the user has the opportunity to clean up the resources that were initialized earlier in the OnThreadInitialization event.
The SmartThreadPool project has a similar project named SmartThreadPoolCE. This version of the SmartThreadPool can be run on Windows CE.
@@ -730,27 +730,27 @@ earlier in the OnThreadInitialization event.
This flag enables the user to poll the Smart Thread Pool / Work Items Group if they are idle.
-Add reference to SmartThreadPoolSL.dll in your code and use it.

Add reference to SmartThreadPoolMono.dll in your code and use it.
Note that the binaries of Mono were compiled on Windows with Visual Studio 2008

The internal performance counter should be used on platforms that don't support Performance Counters, such as WindowsCE, Silverlight, and Mono.
The internal performance counters are variables inside STP that collects the data. To enable them set
the STPStartInfo.EnableLocalPerformanceCounters to true. I use this feature in the new demos. (WindowsCE, Silverlight, and Momo).
The new methods are added to the SmartThreadPool class and implemented using WorkItemsGroup. Their purpose is to simplfy the initiation of a parallel task.
-
This featue let the user specify a timeout for the work item in milliseconds. When the timeout expires the work item is cancelled
- automatically. The cancel works the same as a call to Cancel with the abortExecution argument set to false (This is why the timeout is passive).
To sample the cancel use SmartThreadPool.IsWorkItemCanceled which is a static property, or you can use SmartThreadPool.AbortOnWorkItemOnCancel
+
This featue let the user specify a timeout for the work item to complete in milliseconds. When the timeout expires the work item is cancelled
+ automatically if it didn't complete. The cancel works the same as a call to Cancel with the abortExecution argument set to false (This is why the timeout is passive).
To sample the cancel use SmartThreadPool.IsWorkItemCanceled which is a static property, or you can use SmartThreadPool.AbortOnWorkItemCancel
which check if the current work item is cancelled and if so abort the thread (Thread.CurrentThread.Abort()).
This example shows how to use Timeout
+public class TimeoutExample
+{
+ public void DoWork()
+ {
+ SmartThreadPool stp = new SmartThreadPool();
+
+ ...
+
+ // Queue a work item that will be cancelled within 5 seconds
+ IWorkItemResult wir =
+ stp.QueueWorkItem(
+ new WorkItemInfo() { Timeout = 5*1000 },
+ DoSomething);
+
+ ...
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomething(object state)
+ {
+ ...
+
+ for(...)
+ {
+ // If the work item was cancelled then abort the thread.
+ SmartThreadPool.AbortOnWorkItemCancel();
+ }
+
+ ...
+
+ return result;
+ }
+}
+
THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.