Custom Thread Pooling Template

发表于:2007-06-30来源:作者:点击数: 标签:
Custom Thread Pooling Template Now that we have covered the basics of Visual Studio .NET project types and templates, let us look at how we can write our own custom project template that will generate a thread pool that can be used by the d
Custom Thread Pooling Template
Now that we have covered the basics of Visual Studio .NET project types and templates, let us look at how we can write our own custom project template that will generate a thread pool that can be used by the developer without any modifications at all. Before we start discussing the custom template, let us focus on writing a thread pool class that will be a part of this template.
A thread pool usually consists of two classes: one class that represents a thread, and another class that represents the object that manages a pool of these thread objects. The thread class is fairly simple: it is simply an encapsulation of the built in System.Threading.Thread class, but the thread pool class is slightly more involved. This thread pool class has to create all the threads in the pool, assign them work, and keep track of idle threads so that further work can be assigned to them. Further, the threads in a thread pool are created once, and if a thread is idle, the thread goes into an efficient wait mode (like a sleep call). When there is work to be processed, the thread pool is responsible for bringing an idle thread back into active mode and assigning it work.
Let us start by discussing the thread class first.
public class ThreadPoolThread
{
   public ThreadPoolThread
    (ThreadPool iThreadPool, ThreadPool.DoWork iWorkerMethod)
   {
      mWorkerMethod += iWorkerMethod;

      mThreadPool = iThreadPool;
      mThread = new Thread (new ThreadStart (ThreadFunc));
      mEvent = new AutoResetEvent (false);
      mThread.Start ();
   }

   public void ProcessRequest ()
   {
      mEvent.Set ();
   }

   public void Stop ()
   {
      if (mThread != null)
         mThread.Abort ();
   }

   private void ThreadFunc ()
   {
      WaitHandle[] lWaitHandle = new WaitHandle[1];
      lWaitHandle[0] = mEvent;

      while (true){
         AutoResetEvent.WaitAll (lWaitHandle);
         mWorkerMethod ();
         mThreadPool.ReturnToPool (this);
      }
   }

   private ThreadPool.DoWork mWorkerMethod;
   private Thread mThread;
   private AutoResetEvent mEvent;
   private ThreadPool mThreadPool;
}
This class has four methods, a constructor that sets up the objects required by the class, a method to wake up the thread, a method to kill the thread, and the worker method where the thread will spend its entire life. The ThreadPoolThread object consists of one Thread object and one AutoResetEvent object, which is used to signal an idle thread. The constructor takes in a reference to the thread pool that this thread belongs to, and it also takes in a delegate which is the user defined method that this thread will invoke as part of its processing. The user of this class will pass this delegate, and it typically will be a method that will aclearcase/" target="_blank" >ccept a request (from a socket or from a web service, etc) and process it. Once this delegate method returns, the thread will go into an efficient wait, blocking the event object. This is an efficient wait state that consumes minimal resources. The thread can be brought out of this wait state by the thread pool, by calling its ProcessRequest method. This method simply calls the Set method of the event object, which causes the event object to go into a signaled state. This causes the AutoResetEvent.WaitAll method to come out of a blocking state, and invoke the delegate method. This continues in an infinite loop, until the thread is terminated by the thread pool calling the Stop method.
Let us now look at the thread pool class. Note that this is a custom implementation of the thread pool, which is different from the System.Threading.ThreadPool class. We will start off by discussing the Start and the Stop methods.
public delegate void DoWork ();

public ThreadPool (int iNumThreads)
{
   mNumThreads = iNumThreads;
}

public void Start (DoWork iWorkerMethod)
{
   mFreeList = new ThreadPoolThread [mNumThreads];
   mThreads = new ThreadPoolThread [mNumThreads];
   mFreeThreads = mNumThreads;

   ThreadPoolThread lThread;

   for (int i = 0; i < mNumThreads; i++){
      lThread = new ThreadPoolThread (this, iWorkerMethod);
      mFreeList[i] = mThreads[i] = lThread;
   }
}

public void Stop ()
{
   for (int i = 0; i < mNumThreads; i++)
      mThreads[i].Stop ();
}

private int mNumThreads;
private ThreadPoolThread[] mFreeList;
private ThreadPoolThread[] mThreads;
private int mFreeThreads;
The first thing that the ThreadPool class defines is a delegate that is used by the thread class to call the method that will perform the actual processing, as explained above. The constructor of the thread pool takes in a single parameter which specifies the number of threads that will be created in this pool. The thread pool is started by calling the Start method, which creates a pool of ThreadPoolThread objects, and stores a pointer reference to these objects in the mThreads and mFreeList arrays (we use arrays for simplicity in this sample: a more efficient alternative would be the use of lists). The mThreads array represents all the threads in this pool, and the mFreeList array represents all the threads that are currently free to service requests. All threads start off belonging to the free list. The Stop method simply calls the Stop method on the ThreadPoolThread class that aborts the thread.
Let us now look at the ProcessRequest method that is called by the client when it needs a thread to process a request. Calling this method causes the delegate that was passed to the Start method to be called.
public bool ProcessRequest ()
{
   ThreadPoolThread lThread = null;
   if (mFreeThreads == 0)
      return false;

   lock(this)
   {
      mFreeThreads--;
      for (int i = 0; i < mNumThreads; i++)
         if (mFreeList[i] != null){
            lThread = mFreeList[i];
            mFreeList[i] = null;
            break;
         }
   }

   lThread.ProcessRequest ();
   return true;
}
The ProcessRequest method needs to find a free thread to process the request, and it starts off by checking the value of the mFreeThreads counter, which indicates the number of available free threads. If no threads are available, the methods returns false. If free threads are available, the thread pool iterates through the mFreeList array looking for the first free thread. Once a thread is found, it marks the thread as busy by removing it from the free list. This method then calls the ProcessRequest method on the free thread, that in turns invokes the user delegate. Since this method can be called from multiple threads, we use the C# lock keyword to protect the mFreeList and mFreeThreads objects for concurrent access.
The ReturnToPool method that is used to return a busy thread to the pool of free threads is shown below. This method is similar to the ProcessRequest method, wherein the method attempts to find a free position in the mFreeList array to add the thread to. Again, we use the lock keyword to guard against concurrent access to the mFreeList and mFreeThreads objects.
public void ReturnToPool (ThreadPoolThread iThread)
{
   lock (this)
   {
      mFreeThreads++;
      for (int i = 0; i < mNumThreads; i++)
         if (mFreeList[i] == null){
            mFreeList[i] = iThread;
            break;
         }
   }

   return;
}
   

原文转自:http://www.ltesting.net