Ziff-Davis Enterprise 
DevSource: Microsoft Developer Resource
Add OnsArchitectureLanguagesTechniquesUsing VSForums
 
Home arrow Using VS arrow Page 3 - Appreciating the System ThreadPool
Appreciating the System ThreadPool
By Jon Shemitz

Rate This Article:
Add This Article To:
Appreciating the System ThreadPool - ' Threads and ThreadPools '
( Page 3 of 4 )

When you explicitly create a thread, your thread executes the delegate you passed the constructor, and your thread dies when your delegate returns. By default, your thread is a foreground thread.

By contrast, a TheadPool thread is a background thread that runs a delegate to a method of an object that stores a delegate in an instance field. The pooled thread's Thread delegate executes its stored delegate. When that stored delegate returns, the pooled thread's Thread delegate adds the pooled thread to a ready list within the system ThreadPool, and then waits for its private wait handle to be set, which signals that the pooled thread has a new stored delegate to execute.

ADVERTISEMENT

This helps defer Thread destruction costs until the process terminates. More importantly, the ThreadPool can drastically reduce the number of threads a process needs to create by allowing threads to be reused.

You should explicitly create threads that will run for most of the life of the application, and you should explicitly create threads that will do a lot of computation. The ThreadPool is ideal for threads that come and go without consuming many processor cycles. You should also probably explicitly create threads when you need to change any of the thread's properties, such as Priority or IsBackground; this will always be safer (and easier) than saving and restoring a pool thread's state.

The system executes asynchronous delegates in ThreadPool threads. Calling BeginInvoke on a delegate is both the easiest and most efficient way to pass parameters to a thread delegate and/or to get results back. If you are trying to take advantage of multiprocessing, using BeginInvoke to run delegates asynchronously is an easy way to parallelize a foreach loop.

Be sure to use a Semaphore to keep thread requests in line with the system's set of multicore and/or hyperthreaded processors.

For example, given a declaration like delegate string Fetch(string Resource), the simple thread at the start of this article could be recoded, using the ThreadPool, as

Fetch Fn = delegate(string Filename)
{
  using (StreamReader Reader = new StreamReader(Filename))
    return Reader.ReadToEnd();
};

IAsyncResult Async = Fn.BeginInvoke(@"..\..\Program.cs", null, null);
// ...
string Source = Fn.EndInvoke(Async);

This is functionally identical to creating, starting, and joining the Thread at the start of this article, but takes a bit less code (you don't have to Start the Thread) and takes less time because you don't have to finalize the thread, and you may not even have to create it.

In real code, you will probably already have a ReadFile or a GetURL method that takes a resource name and returns a string. And you will asynchronously call a Fetch delegate to a named method, instead of using an anonymous method. Calling delegates asynchronously is the best way to pass values to an agent and collect its results. But when your agent will do something without returning any results, you might as well use an anonymous method in lower-level code that calls the ThreadPool directly.

The ThreadPool.QueueUserWorkItem static method takes a WaitCallback delegate and executes it. A WaitCallback delegate is a delegate to a method that takes a single, object parameter and returns nothing: delegate void WaitCallback (object state). The delegate executes right away, if a thread is available or the pool size is within the configurable limits; if the ThreadPool is empty, the delegate waits until a thread is returned to the pool. (The ThreadPool has static methods to get and set the size of the pool.)

Because a WaitCallback delegate only gets a single, object parameter, it's pretty ideally suited to an anonymous method that can carry all its thread state information in captured variables. For example, you may have accumulated a long Report string that you need to write to a given Filename. Maybe running WriteFile(Filename, Report) will take milliseconds of wall-clock time, but it will always succeed (or handle errors appropriately). Using ThreadPool.QueueUserWorkItem, you can (re)use a thread to capture the Filename and Report strings, and write the file in a background thread:

ThreadPool.QueueUserWorkItem(delegate
{
    WriteFile(Filename, Report);
});

Using anonymous methods as thread delegates that capture method parameters and/or local variables maximizes information hiding and minimizes boiler plate. You don't even have a private delegate type visible outside the method that spawns an agent.



 
 
>>> More Using VS Articles          >>> More By Jon Shemitz
 



DevSource video
Devsource Video Series
Manipulating Society through Technology
Jeremy Bailenson, Director of the Virtual Human Interaction Lab at Stanford University, talks about virtual reality, avatars, Moore's law, how real world behaviors influence online reality, and societal manipulation through technology!
>> Play video
>> Read article
>> See all videos
DevLife Blog

Julia explores the Robotics Studio! (It's for more than you think.)

MSDev Blog

Messages for Bill Gates!

Make it Work
.NET makes runtime type checking a breeze. See what Peter has to say about it in this week's tips!
News
Microsoft Counts on App Support for Vista
Microsoft has taken pains to demonstrate that Windows Vista will have ample application support.
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.