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 @@
  • Added support for WinCE (limited)
  • Added IsIdle flag to the SmartThreadPool and to the IWorkItemsGroup
  • Added support for Action<T> - and Func<T> (strong typed work items)
  • + and Func<T> (strong typed work items). (See section 3)

    @@ -396,7 +396,7 @@ SmartThreadPool.WaitAny(new IWaitableResult[] { wir1, wir2});

    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.

    -
  • A work item can be cancelled. (This feature is enhanced) +
  • A work item can be cancelled. (This feature is enhanced)

     This feature enables to cancel work items.

    @@ -502,7 +502,7 @@ Here is an example of a cooperative work item: } } -
  • The caller thread's context is used when the work item is executed (limited). +
  • The caller thread's context is used when the work item is executed (limited).

    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:

    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.

    -
  • The user can choose to automatically dispose off the state object that accompanies the work item. +
  • The user can choose to automatically dispose off the state object that accompanies the work item.

    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.

    @@ -548,7 +548,7 @@ public enum CallToPostExecute Note that this feature only applies to the state argument that comes with WorkItemCallback, it doesn't apply to the arguments supplied in Action<...> and Func<...> arguments!!!

    -
  • The user can wait for the Smart Thread Pool / Work Items Group +
  • The user can wait for the Smart Thread Pool / Work Items Group to become idle.

    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.

    -
  • 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. +
  • 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.

    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.

    @@ -571,7 +571,7 @@ public enum CallToPostExecute

    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 have priority. +
  • Work items have priority.

    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.

    -
  • The caller thread's HTTP context can be used when the work item is executed. +
  • The caller thread's HTTP context can be used when the work item is executed.

    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.

    -
  • Work items group. +
  • Work items group.

    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.

    -
  • The caller can create thread pools and work items groups in suspended state. +
  • The caller can create thread pools and work items groups in suspended state.

    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.

    -
  • Threads have priority. +
  • Threads have priority.

    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 :-(.

    -
  • The MaxThreads/MinThreads/Concurrency can +
  • The 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.

    -
  • Improved Cancel behavior. +
  • Improved Cancel behavior.

    (See section 5)

    -
  • Threads initialization and termination generate events.

    +

  • Threads initialization and termination generate events.

    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.

  • -
  • Supports Windows CE (limited)
  • +
  • Supports Windows CE (limited)
  • 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.

    -
  • SmartThreadPool and IWorkItemsGroup has IsIdle flag
  • +
  • SmartThreadPool and IWorkItemsGroup has IsIdle flag
  • This flag enables the user to poll the Smart Thread Pool / Work Items Group if they are idle.

    -
  • Supports Action<T> and Func<T> (strongly typed work items)
  • +
  • Supports Action<T> and Func<T> (strongly typed work items)
  • (See section 3)

    -
  • Supports Silverlight
  • +
  • Supports Silverlight
  • Add reference to SmartThreadPoolSL.dll in your code and use it.

    -
  • Supports Mono
  • +
  • Supports Mono
  • 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

    -
  • Internal performance counters
  • +
  • Internal performance counters
  • 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).

    -
  • Join, Choice, and Pipe
  • +
  • Join, Choice, and Pipe
  • The new methods are added to the SmartThreadPool class and implemented using WorkItemsGroup. Their purpose is to simplfy the initiation of a parallel task.

      @@ -759,11 +759,12 @@ to simplfy the initiation of a parallel task.

    • Pipe - Executes several work items sequently and wait until the last work item completes. (Pipe example)

    -

  • Work item timeout (passive)
  • -

    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 +

  • Work item timeout (passive)
  • +

    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()).

    +See timeout example below

    When to use?

    @@ -1294,6 +1295,42 @@ public class PipeExample }
    +

    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;
    +    }
    +}
    +

    Disclaimer

    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.