mirror of
https://github.com/farcasclaudiu/SmartThreadPool.git
synced 2026-06-22 09:01:19 +03:00
Updated document
This commit is contained in:
+65
-28
@@ -92,7 +92,7 @@
|
||||
<li><A href="#Feature20">Added support for WinCE (limited)</A></li>
|
||||
<li><A href="#Feature21">Added IsIdle flag to the SmartThreadPool and to the IWorkItemsGroup</A></li>
|
||||
<li><A href="#Feature3">Added support for Action<T>
|
||||
and Func<T> (strong typed work items)</A></li>
|
||||
and Func<T> (strong typed work items). (See section 3)</A></li>
|
||||
</OL>
|
||||
|
||||
<h2>
|
||||
@@ -396,7 +396,7 @@ SmartThreadPool.WaitAny(new IWaitableResult[] { wir1, wir2});</PRE>
|
||||
<P><B>Note</B> that in order to use <code>WaitAny()</code> and <code>WaitAll()</code>, you need to work in MTA, because internally I use <code>WaitHandle.WaitAny()</code> and <code>WaitHandle.WaitAll()</code> which requires it. If you don't do that, the methods will throw an exception to remind you.</P>
|
||||
<P><B>Also note</B> that Windows supports <code>WaitAny()</code> of up to 64 handles. The <code>WaitAll()</code> is more flexible and I re-implemented it so it is not limited to 64 handles.</P>
|
||||
<P>See in the examples section below the code snippets for <A href="#WaitForAllExample">WaitAll</A> and <A href="#WaitForAnyExample">WaitAny</A>.</P>
|
||||
<li><A name=#Feature5><B>A work item can be cancelled.</B></A> (This feature is enhanced)
|
||||
<li><A name=Feature5><B>A work item can be cancelled.</B></A> (This feature is enhanced)
|
||||
<P>
|
||||
This feature enables to cancel work items.
|
||||
</P>
|
||||
@@ -502,7 +502,7 @@ Here is an example of a cooperative work item:
|
||||
}
|
||||
}</PRE>
|
||||
|
||||
<li><A name=#Feature6><B>The caller thread's context is used when the work item is executed (limited)</B></A>.
|
||||
<li><A name=Feature6><B>The caller thread's context is used when the work item is executed (limited)</B></A>.
|
||||
<P>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 <code>CompressedStack</code> 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:</P>
|
||||
<UL>
|
||||
<li><code>CurrentCulture</code> - The culture of the thread.
|
||||
@@ -518,12 +518,12 @@ Here is an example of a cooperative work item:
|
||||
<li>The work item is executed.
|
||||
<li>The current old thread context is restored. </li></OL>
|
||||
<p></p>
|
||||
<li><B>Usage of <A name=#Feature7>minimum number of Win32 event handles, so the handle count of the application won't explode</A></B>.
|
||||
<li><B>Usage of <A name=Feature7>minimum number of Win32 event handles, so the handle count of the application won't explode</A></B>.
|
||||
<P>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 <code>Close()</code> method of <code>ManualResetEvent</code> 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.</P>
|
||||
<P>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 <code>WaitHandle</code> but use them internally and then close them.</P>
|
||||
<P>In order to create fewer handles, I created the <code>ManualResetEvent</code> objects only when the user asks for them (lazy creation). For example, if you don't use the <code>GetResult()</code> of the <code>IWorkItemResult</code> interface then a handle is not created. Using <code>SmartThreadPool.WaitAll()</code> and <code>SmartThreadPool.WaitAny()</code> creates a handle.</P>
|
||||
<P>The work item queue created a lot of handles since each new wait for a work item created a new <code>ManualResetEvent</code>. 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 <code>ManualResetEvent</code> 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.</P>
|
||||
<li><A name=#Feature8><B>Work item can have a PostExecute callback. This is a method that will be called right after the work item execution has been completed</B></A>.
|
||||
<li><A name=Feature8><B>Work item can have a PostExecute callback. This is a method that will be called right after the work item execution has been completed</B></A>.
|
||||
<P>A <code>PostExecute</code> 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 <code>PostExecute</code> is called. The options are represented in the <code>CallToPostExecute</code> flagged enumerator:</P><PRE lang="cs">[Flags]
|
||||
public enum CallToPostExecute
|
||||
{
|
||||
@@ -540,7 +540,7 @@ public enum CallToPostExecute
|
||||
<li><code>Always</code> – Always run the <code>PostExecute</code>. </li></UL>
|
||||
<P>The <code>SmartThreadPool</code> has a default <code>CallToPostExecute</code> value of <code>CallToPostExecute.Always</code>. This can be changed during the construction of the <code>SmartThreadPool</code> in the <code>STPStartInfo</code> class argument. Another way to give the <code>CallToPostExecute</code> value is in one of the <code>SmartThreadPool.QueueWorkItem</code> overloads. Note that as opposed to the <code>WorkItem</code> execution, if an exception has been thrown during the <code>PostExecute</code>, then it is ignored. The <code>PostExecute</code> is a delegate with the following signature:</P><PRE lang="cs">public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);</PRE>
|
||||
<P>As you can see, the <code>PostExecute</code> receives as an argument of type <code>IWorkItemResult</code>. It can be used to get the result of the work item, or any other information made available by the <code>IWorkItemResult</code> interface.</P>
|
||||
<li><A name=#Feature9><B>The user can choose to automatically dispose off the state object that accompanies the work item</B></A>.
|
||||
<li><A name=Feature9><B>The user can choose to automatically dispose off the state object that accompanies the work item</B></A>.
|
||||
<P>When the user calls the <code>QueueWorkItem</code>, he/she can provide a state object. The state object usually stores specific information, such as arguments, that should be used within the <code>WorkItemCallback</code> delegate.</P>
|
||||
<P>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.</P>
|
||||
<P>For this reason, I added a boolean to the <code>SmartThreadPool</code> that indicates to call <code>Dispose</code> on the state object when the work item has been completed. The boolean is initialized when the thread pool is constructed with the <code>STPStartInfo</code>. The <code>Dispose</code> is called only if the state object implements the <code>IDisposable</code> interface. The <code>Dispose</code> is called after the <code>WorkItem</code> has been completed and its <code>PostExecute</code> has run (if a <code>PostExecute</code> exists). The state object is disposed even if the work item has been canceled or the thread pool has been shutdown.</P>
|
||||
@@ -548,7 +548,7 @@ public enum CallToPostExecute
|
||||
<b>Note that this feature only applies to the <code>state</code> argument that
|
||||
comes with WorkItemCallback, it doesn't apply to the
|
||||
arguments supplied in <code>Action<...></code> and <code>Func<...></code> arguments!!!</b></p>
|
||||
<li><A name=#Feature10><B>The user can wait for the Smart Thread Pool / Work Items Group
|
||||
<li><A name=Feature10><B>The user can wait for the Smart Thread Pool / Work Items Group
|
||||
to become idle. </B></A>
|
||||
<P>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
|
||||
<P>The <code>SmartThreadPool</code> 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.</P>
|
||||
<P>The <code>WaitForIdle()</code> mechanism works with a private <code>ManualResetEvent</code>. When a work item is queued, the <code>ManualResetEvent</code> is reset (changed to non signaled state). When the work items count becomes zero (initial state of the Smart Thread Pool), the <code>ManualResetEvent</code> is set (changed to signaled state). The <code>WaitForIdle()</code> method just waits for the <code>ManualResetEvent</code> to implement its functionality.</P>
|
||||
<P>See the <A href="#WaitForIdleExample">example</A> below.</P>
|
||||
<li><A name=#Feature11><B>The exception handling is changed, so if a work item throws an exception, it is rethrown at GetResult(), rather than firing an UnhandledException event. Note that PostExecute exceptions are always ignored</B></A>.
|
||||
<li><A name=Feature11><B>The exception handling is changed, so if a work item throws an exception, it is rethrown at GetResult(), rather than firing an UnhandledException event. Note that PostExecute exceptions are always ignored</B></A>.
|
||||
<P>After I did some reading about delegates and their implementation, I decided to change the way the <code>SmartThreadPool</code> treats exceptions. In the previous versions, I used an event driven mechanism. Entities were registered to the <code>SmartThreadPool.UnhandledException</code> event, and when a work item threw an exception, this event was fired. This is the behavior of Toub’s thread pool.</P>
|
||||
<P>.NET delegates behave differently. Instead of using an event driven mechanism, it re-throws the exception of the delegated method at the <code>EndInvoke()</code>. Similarly, the <code>SmartThreadPool</code> exception mechanism is changed so that exceptions are no longer fired by the <code>UnhandledException</code> event, but rather re-thrown again when <code>IWorkItemResult.GetResult()</code> is called.</P>
|
||||
<P>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 <code>GetResult()</code> 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.</P>
|
||||
@@ -571,7 +571,7 @@ public enum CallToPostExecute
|
||||
<P>Note that <code>PostExecute</code> is called, as and when needed, even if the work item has thrown an exception. Of course, <code>PostExecute</code> implementation should handle exceptions if it calls <code>GetResult()</code>.</P>
|
||||
<P>Also note that if the <code>PostExecute</code> throws an exception then its exception is ignored.</P>
|
||||
<P>See the <A href="#CatchExceptionExample">example</A> below.</P>
|
||||
<li><A name=#Feature12><B>Work items have priority</B></A>.
|
||||
<li><A name=Feature12><B>Work items have priority</B></A>.
|
||||
<P>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:</P><PRE lang="cs">public enum WorkItemPriority
|
||||
{
|
||||
Lowest,
|
||||
@@ -583,10 +583,10 @@ public enum CallToPostExecute
|
||||
<P>The default priority is <code>Normal</code>.</P>
|
||||
<P>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.</P>
|
||||
<P>This is the easiest solution to sort the work items.</P>
|
||||
<li><A name=#Feature13><B>The caller thread's HTTP context can be used when the work item is executed</B></A>.
|
||||
<li><A name=Feature13><B>The caller thread's HTTP context can be used when the work item is executed</B></A>.
|
||||
<P>This feature improves 6, and was implemented by Steven T. I just replaced my code with that implementation.</P>
|
||||
<P>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.</P>
|
||||
<li><A name=#Feature14><B>Work items group</B></A>.
|
||||
<li><A name=Feature14><B>Work items group</B></A>.
|
||||
<P>This feature enables the user to execute a group of work items specifying the maximum level of concurrency.</P>
|
||||
<P>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) {...}
|
||||
<P>To accomplish the concurrency level, the <code>WorkItemsGroup</code> registers to the completion event of its work items. The event is used internally and is not exposed to the user. Once registered, the <code>WorkItemsGroup</code> will get an event when its work item is completed. The event will trigger the <code>WorkItemsGroup</code> to queue more work items into the <code>SmartThreadPool</code>. The event is the only way to accomplish the concurrency level. When I tried to do it with <code>PostExecute</code> I got fluctuating <code>WaitForIdle</code>.</P>
|
||||
<P>Another advantage of the <code>WorkItemsGroup</code> 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 <code>WorkItemsGroup</code> does so by attaching an object to each one of its work items that indicates if the <code>WorkItemsGroup</code> has been cancelled. When a work item is about to be executed, it is asked for its current state (<code>InQueue</code>, <code>InProgress</code>, <code>Completed</code>, or <code>Canceled</code>). The final state considers this object's value to know if the work item was cancelled.</P>
|
||||
<p>
|
||||
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 <A href="#ConjuctionPointExample">conjuction point example</A>.</p>
|
||||
@@ -643,14 +643,14 @@ void Print(Printer printer, Document document) {...}
|
||||
<p>
|
||||
See WorkItemsGroupDemo demo in the source code solution.</p>
|
||||
<p><IMG src="WorkItemsGroup.jpg" width="636" height="749" /></p>
|
||||
<li><A name=#Feature15><B>The caller can create thread pools and work items groups in suspended state</B></A>.
|
||||
<li><A name=Feature15><B>The caller can create thread pools and work items groups in suspended state</B></A>.
|
||||
<P>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.</P>
|
||||
<P>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 <code>Start()</code> method. The same method exists in the Work Items Group for the same purpose.</P>
|
||||
<P>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.</P>
|
||||
<li><A name=#Feature16><B>Threads have priority</B></A>.
|
||||
<li><A name=Feature16><B>Threads have priority</B></A>.
|
||||
<P>The <code>STPStartInfo</code> contains a property that defines the priority in which the threads are started in the <code>SmartThreadPool</code>. Use it if you know what you are doing. Playing with threads priority may end up with dead locks, live lock, and days locked :-(.</P>
|
||||
|
||||
<li><A name=#Feature17><b>The <code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> can
|
||||
<li><A name=Feature17><b>The <code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> can
|
||||
be changed at run time</b></A>.
|
||||
<p>
|
||||
This addition allows the user to control the concurrency of work items execution.
|
||||
@@ -684,10 +684,10 @@ void Print(Printer printer, Document document) {...}
|
||||
|
||||
<p>The number of <code>MaxThreads</code> must be greater or equal to <code>MinThreads</code>. If <code>MaxThreads</code> is set to a number lower than <code>MinThreads</code> than <code>MinThreads</code> is also set to the new value of <code>MaxThreads</code>. And vice versa for <code>MinThreads</code>.</p>
|
||||
|
||||
<li><A name=#Feature18><B>Improved Cancel behavior</b></a>.
|
||||
<li><A name=Feature18><B>Improved Cancel behavior</b></a>.
|
||||
<p>
|
||||
<A href="#Feature5">(See section 5)</A></p>
|
||||
<li><A name=#Feature19><B>Threads initialization and termination generate events</B></A>.<p>
|
||||
<li><A name=Feature19><B>Threads initialization and termination generate events</B></A>.<p>
|
||||
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.
|
||||
</p></li>
|
||||
<li><a name=#Feature20><b>Supports Windows CE (limited)</b></a></li>
|
||||
<li><a name=Feature20><b>Supports Windows CE (limited)</b></a></li>
|
||||
|
||||
<p>The SmartThreadPool project has a similar project named SmartThreadPoolCE. This
|
||||
version of the SmartThreadPool can be run on Windows CE.</p>
|
||||
@@ -730,27 +730,27 @@ earlier in the OnThreadInitialization event.
|
||||
|
||||
<P><IMG src="WindowsCE.jpg" width="346" height="586"></P>
|
||||
|
||||
<li><a name=#Feature21><b>SmartThreadPool and IWorkItemsGroup has IsIdle flag</b></a></li>
|
||||
<li><a name=Feature21><b>SmartThreadPool and IWorkItemsGroup has IsIdle flag</b></a></li>
|
||||
<p>This flag enables the user to poll the Smart Thread Pool / Work Items Group if they are idle.</p>
|
||||
<li><a name=#Feature22><b>Supports Action<T> and Func<T> (strongly typed work items)</b></a></li>
|
||||
<li><a name=Feature22><b>Supports Action<T> and Func<T> (strongly typed work items)</b></a></li>
|
||||
<p>
|
||||
<a href="#Feature3">(See section 3)</a></p>
|
||||
<li><a name=#Feature23><b>Supports Silverlight</b></a></li>
|
||||
<li><a name=Feature23><b>Supports Silverlight</b></a></li>
|
||||
<p></p>
|
||||
<p>Add reference to SmartThreadPoolSL.dll in your code and use it.</p>
|
||||
<P><IMG src="Silverlight.jpg" width="570" height="664"></P>
|
||||
|
||||
<li><a name=#Feature24><b>Supports Mono</b></a></li>
|
||||
<li><a name=Feature24><b>Supports Mono</b></a></li>
|
||||
<p>Add reference to SmartThreadPoolMono.dll in your code and use it.</p>
|
||||
<p>Note that the binaries of Mono were compiled on Windows with Visual Studio 2008</p>
|
||||
<P><IMG src="Mono.jpg" width="840" height="525"></P>
|
||||
|
||||
<li><a name=#Feature25><b>Internal performance counters</b></a></li>
|
||||
<li><a name=Feature25><b>Internal performance counters</b></a></li>
|
||||
<p>The internal performance counter should be used on platforms that don't support Performance Counters,
|
||||
such as WindowsCE, Silverlight, and Mono.</p>
|
||||
<p>The internal performance counters are variables inside STP that collects the data. To enable them set
|
||||
the <code>STPStartInfo.EnableLocalPerformanceCounters</code> to true. I use this feature in the new demos. (WindowsCE, Silverlight, and Momo).</p>
|
||||
<li><a name=#Feature26><b>Join, Choice, and Pipe</b></a></li>
|
||||
<li><a name=Feature26><b>Join, Choice, and Pipe</b></a></li>
|
||||
<p>The new methods are added to the SmartThreadPool class and implemented using WorkItemsGroup. Their purpose is
|
||||
to simplfy the initiation of a parallel task.</p>
|
||||
<ul>
|
||||
@@ -759,11 +759,12 @@ to simplfy the initiation of a parallel task.</p>
|
||||
<li>Pipe - Executes several work items sequently and wait until the last work item completes. <A href="#PipeExample">(Pipe example)</A></li>
|
||||
</ul>
|
||||
<p>
|
||||
<li><a name=#Feature27><b>Work item timeout (passive)</b></a></li>
|
||||
<p>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 <code>abortExecution</code> argument set to false (This is why the timeout is passive).</p>
|
||||
<p>To sample the cancel use <code>SmartThreadPool.IsWorkItemCanceled</code> which is a static property, or you can use <code>SmartThreadPool.AbortOnWorkItemOnCancel</code>
|
||||
<li><a name=Feature27><b>Work item timeout (passive)</b></a></li>
|
||||
<p>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 <code>abortExecution</code> argument set to false (This is why the timeout is passive).</p>
|
||||
<p>To sample the cancel use <code>SmartThreadPool.IsWorkItemCanceled</code> which is a static property, or you can use <code>SmartThreadPool.AbortOnWorkItemCancel</code>
|
||||
which check if the current work item is cancelled and if so abort the thread (<code>Thread.CurrentThread.Abort()</code>).</p>
|
||||
See timeout <A href="#TimeoutExample">example</A> below
|
||||
</p>
|
||||
</OL>
|
||||
<H2>When to use?</H2>
|
||||
@@ -1294,6 +1295,42 @@ public class <A name=PipeExample>PipeExample</A>
|
||||
|
||||
}</PRE>
|
||||
|
||||
<P>This example shows how to use Timeout</P><PRE lang="cs">
|
||||
public class <A name=TimeoutExample>TimeoutExample</A>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}</PRE>
|
||||
|
||||
|
||||
<H2>Disclaimer</H2>
|
||||
<P>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.</P>
|
||||
|
||||
Reference in New Issue
Block a user