Раздел «Технологии программирования».ThreadPool:
You can use thread pooling to make much more efficient use of multiple threads, depending on your application. Many applications use multiple threads, but often those threads spend a great deal of time in the sleeping state waiting for an event to occur. Other threads might enter a sleeping state and be awakened only periodically to poll for a change or update status information before going to sleep again. Using thread pooling provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks rather than thread management.

The .NET Framework uses thread pools for several purposes: asynchronous calls, System.Net socket connections, asynchronous I/O completion, and timers and registered wait operations, among others.

You use the thread pool by calling QueueUserWorkItem? from managed code (or CorQueueUserWorkItem? from unmanaged code) and passing a WaitCallback? delegate wrapping the method that you want to add to the queue. You can also queue work items that are related to a wait operation to the thread pool by using RegisterWaitForSingleObject? and passing a WaitHandle? that, when signaled or when timed out, raises a call to the method wrapped by the WaitOrTimerCallback? delegate. In both cases, the thread pool uses or creates a background thread to invoke the callback method.

There is only one ThreadPool object per process. The thread pool is created the first time you call QueueUserWorkItem?, or when a timer or registered wait operation queues a callback method. One thread monitors all tasks that have been queued to the thread pool. When a task has completed, a thread from the thread pool executes the corresponding callback method. There is no way to cancel a work item after it has been queued.

The number of operations that can be queued to the thread pool is limited only by available memory; however, the thread pool will enforce a limit on the number of threads it allows to be active in the process simultaneously (which is subject to the number of CPUs and other considerations). Each thread uses the default stack size, runs at the default priority, and is in the multithreaded apartment. If one of the threads becomes idle (as when waiting on an event) in managed code, the thread pool injects another worker thread to keep all the processors busy. If all thread pool threads are constantly busy, but there is pending work in the queue, the thread pool will, after some period of time, create another worker thread. However, the number of threads will never exceed the maximum value. The ThreadPool also switches to the correct AppDomain? when executing ThreadPool callbacks.

There are several scenarios in which it is appropriate to create and manage your own threads instead of using the ThreadPool. You should do so:

ThreadPool Examples The three code examples that follow demonstrate the QueueUserWorkItem? and RegisterWaitForSingleObject? methods. The first example queues a very simple task, represented by the ThreadProc? method, using QueueUserWorkItem?.

using System;
using System.Threading;
public class Example {
    public static void Main() {
        // Queue the task.
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
        
        Console.WriteLine("Main thread does some work, then sleeps.");
        // If you comment out the Sleep, the main thread exits before
        // the thread pool task runs.  The thread pool uses background
        // threads, which do not keep the application running.  (This
        // is a simple example of a race condition.)
        Thread.Sleep(1000);

        Console.WriteLine("Main thread exits.");
    }

    // This thread procedure performs the task.
    static void ThreadProc(Object stateInfo) {
        // No state object was passed to QueueUserWorkItem, so 
        // stateInfo is null.
        Console.WriteLine("Hello from the thread pool.");
    }
}

Supplying Task Data for QueueUserWorkItem?

The following code example uses QueueUserWorkItem? to queue a task and supply the data for the task.

using System;
using System.Threading;

// TaskInfo holds state information for a task that will be
// executed by a ThreadPool thread.
public class TaskInfo {
    // State information for the task.  These members
    // can be implemented as read-only properties, read/write
    // properties with validation, and so on, as required.
    public string Boilerplate;
    public int Value;

    // Public constructor provides an easy way to supply all
    // the information needed for the task.
    public TaskInfo(string text, int number) {
        Boilerplate = text;
        Value = number;
    }
}

public class Example {
    public static void Main() {
        // Create an object containing the information needed
        // for the task.
        TaskInfo ti = new TaskInfo("This report displays the number {0}.", 42);

        // Queue the task and data.
        if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti)) {    
            Console.WriteLine("Main thread does some work, then sleeps.");

            // If you comment out the Sleep, the main thread exits before
            // the ThreadPool task has a chance to run.  ThreadPool uses 
            // background threads, which do not keep the application 
            // running.  (This is a simple example of a race condition.)
            Thread.Sleep(1000);

            Console.WriteLine("Main thread exits.");
        }
        else {
            Console.WriteLine("Unable to queue ThreadPool request."); 
        }
    }

    // The thread procedure performs the independent task, in this case
    // formatting and printing a very simple report.
    //
    static void ThreadProc(Object stateInfo) {
        TaskInfo ti = (TaskInfo) stateInfo;
        Console.WriteLine(ti.Boilerplate, ti.Value); 
    }
}

RegisterWaitForSingleObject

The following example demonstrates several threading features.

* Queuing tasks for execution by ThreadPool threads, with RegisterWaitForSingleObject?. * Signaling the tasks to execute, with AutoResetEvent?. * Handling both time-outs and signals with a WaitOrTimerCallback? delegate. * Canceling a queued task with RegisteredWaitHandle?.

using System;
using System.Threading;

// TaskInfo contains data that will be passed to the callback
// method.
public class TaskInfo {
    public RegisteredWaitHandle Handle = null;
    public string OtherInfo = "default";
}

public class Example {
    public static void Main(string[] args) {
        // The main thread uses AutoResetEvent to signal the
        // registered wait handle, which executes the callback
        // method.
        AutoResetEvent ev = new AutoResetEvent(false);

        TaskInfo ti = new TaskInfo();
        ti.OtherInfo = "First task";
        // The TaskInfo for the task includes the registered wait
        // handle returned by RegisterWaitForSingleObject.  This
        // allows the wait to be terminated when the object has
        // been signaled once (see WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject(
            ev,
            new WaitOrTimerCallback(WaitProc),
            ti,
            1000,
            false
        );

        // The main thread waits three seconds, to demonstrate the
        // time-outs on the queued thread, and then signals.
        Thread.Sleep(3100);
        Console.WriteLine("Main thread signals.");
        ev.Set();

        // The main thread sleeps, which should give the callback
        // method time to execute.  If you comment out this line, the
        // program usually ends before the ThreadPool thread can execute.
        Thread.Sleep(1000);
        // If you start a thread yourself, you can wait for it to end
        // by calling Thread.Join.  This option is not available with 
        // thread pool threads.
    }
   
    // The callback method executes when the registered wait times out,
    // or when the WaitHandle (in this case AutoResetEvent) is signaled.
    // WaitProc unregisters the WaitHandle the first time the event is 
    // signaled.
    public static void WaitProc(object state, bool timedOut) {
        // The state object must be cast to the correct type, because the
        // signature of the WaitOrTimerCallback delegate specifies type
        // Object.
        TaskInfo ti = (TaskInfo) state;

        string cause = "TIMED OUT";
        if (!timedOut) {
            cause = "SIGNALED";
            // If the callback method executes because the WaitHandle is
            // signaled, stop future execution of the callback method
            // by unregistering the WaitHandle.
            if (ti.Handle != null)
                ti.Handle.Unregister(null);
        } 

        Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
            ti.OtherInfo, 
            Thread.CurrentThread.GetHashCode().ToString(), 
            cause
        );
    }
}
-- AndreyUstyuzhanin - 06 Apr 2004