使用线程池 Thread Pooling

    《Windows核心编程》中第11章讲到线程池的使用,可惜,总是看得不大懂。是翻译的问题还是我理解的问题?
    于是,只好求助MSDN,找几篇关于线程池的文章来看看。找到一篇在.Net中线程池应用的,觉得还可以,不太难理解。MSDN默认的字体太小了,看得不太舒服,转过来。


-------------------------------------------------------

Thread Pooling

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. In fact, if you have a number of short tasks that require more than one thread, using the ThreadPool class is the easiest and best way to take advantage of multiple threads. Using a thread pool enables the system to optimize this for better throughput(生产能力) not only for this process but also with respect to other processes on the computer, something your application will know nothing about. Using a thread pool enables the system to optimize thread time slices taking into account all the current processes on your computer.

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 ThreadPool.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 ThreadPool.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.

You can also use the unsafe methods ThreadPool.UnsafeQueueUserWorkItem and ThreadPool.UnsafeRegisterWaitForSingleObject when you know that the caller's stack is irrelevant(不相关的) to any security checks performed during the execution of the queued task. QueueUserWorkItem and RegisterWaitForSingleObject both capture the caller's stack, which is merged(合并,并入) into the stack of the thread pool thread when the thread pool thread starts to execute a task. If a security check is required, that entire stack must be checked. Although the check provides safety, it also has a performance cost. Using the Unsafe method calls does not provide complete safety, but it will provide better performance.

There is only one ThreadPool object per process. The thread pool is created the first time you call ThreadPool.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:

  • If you require a task to have a particular priority.
  • If you have a task that might run a long time (and therefore block other tasks).
  • If you need to place threads into a single-threaded apartment (all ThreadPool threads are in the multithreaded apartment).
  • If you need to have a stable(稳定的) identity associated with the thread. For example, you might want to use a dedicated thread to abort that thread, suspend it, or discover it by name.

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.

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

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

ExpandedBlockEnd.gif}

 

Supplying Task Data for QueueUserWorkItem

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

None.gif using  System;
None.gif
using  System.Threading;
None.gif
None.gif
//  TaskInfo holds state information for a task that will be
None.gif
//  executed by a ThreadPool thread.
ExpandedBlockStart.gifContractedBlock.gif
public   class  TaskInfo  dot.gif {
InBlock.gif    
// State information for the task.  These members
InBlock.gif    
// can be implemented as read-only properties, read/write
InBlock.gif    
// properties with validation, and so on, as required.
InBlock.gif
    public string Boilerplate;
InBlock.gif    
public int Value;
InBlock.gif
InBlock.gif    
// Public constructor provides an easy way to supply all
InBlock.gif    
// the information needed for the task.
ExpandedSubBlockStart.gifContractedSubBlock.gif
    public TaskInfo(string text, int number) dot.gif{
InBlock.gif        Boilerplate 
= text;
InBlock.gif        Value 
= number;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  Example  dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public static void Main() dot.gif{
InBlock.gif        
// Create an object containing the information needed
InBlock.gif        
// for the task.
InBlock.gif
        TaskInfo ti = new TaskInfo("This report displays the number {0}."42);
InBlock.gif
InBlock.gif        
// Queue the task and data.
ExpandedSubBlockStart.gifContractedSubBlock.gif
        if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti)) dot.gif{    
InBlock.gif            Console.WriteLine(
"Main thread does some work, then sleeps.");
InBlock.gif
InBlock.gif            
// If you comment out the Sleep, the main thread exits before
InBlock.gif            
// the ThreadPool task has a chance to run.  ThreadPool uses 
InBlock.gif            
// background threads, which do not keep the application 
InBlock.gif            
// running.  (This is a simple example of a race condition.)
InBlock.gif
            Thread.Sleep(1000);
InBlock.gif
InBlock.gif            Console.WriteLine(
"Main thread exits.");
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
else dot.gif{
InBlock.gif            Console.WriteLine(
"Unable to queue ThreadPool request."); 
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

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

ExpandedBlockEnd.gif}


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.
    None.gif using  System;
    None.gif
    using  System.Threading;
    None.gif
    None.gif
    //  TaskInfo contains data that will be passed to the callback
    None.gif
    //  method.
    ExpandedBlockStart.gifContractedBlock.gif
    public   class  TaskInfo  dot.gif {
    InBlock.gif    
    public RegisteredWaitHandle Handle = null;
    InBlock.gif    
    public string OtherInfo = "default";
    ExpandedBlockEnd.gif}

    None.gif
    ExpandedBlockStart.gifContractedBlock.gif
    public   class  Example  dot.gif {
    ExpandedSubBlockStart.gifContractedSubBlock.gif    
    public static void Main(string[] args) dot.gif{
    InBlock.gif        
    // The main thread uses AutoResetEvent to signal the
    InBlock.gif        
    // registered wait handle, which executes the callback
    InBlock.gif        
    // method.
    InBlock.gif
            AutoResetEvent ev = new AutoResetEvent(false);
    InBlock.gif
    InBlock.gif        TaskInfo ti 
    = new TaskInfo();
    InBlock.gif        ti.OtherInfo 
    = "First task";
    InBlock.gif        
    // The TaskInfo for the task includes the registered wait
    InBlock.gif        
    // handle returned by RegisterWaitForSingleObject.  This
    InBlock.gif        
    // allows the wait to be terminated when the object has
    InBlock.gif        
    // been signaled once (see WaitProc).
    InBlock.gif
            ti.Handle = ThreadPool.RegisterWaitForSingleObject(
    InBlock.gif            ev,
    InBlock.gif            
    new WaitOrTimerCallback(WaitProc),
    InBlock.gif            ti,
    InBlock.gif            
    1000,
    InBlock.gif            
    false
    InBlock.gif        );
    InBlock.gif
    InBlock.gif        
    // The main thread waits three seconds, to demonstrate the
    InBlock.gif        
    // time-outs on the queued thread, and then signals.
    InBlock.gif
            Thread.Sleep(3100);
    InBlock.gif        Console.WriteLine(
    "Main thread signals.");
    InBlock.gif        ev.Set();
    InBlock.gif
    InBlock.gif        
    // The main thread sleeps, which should give the callback
    InBlock.gif        
    // method time to execute.  If you comment out this line, the
    InBlock.gif        
    // program usually ends before the ThreadPool thread can execute.
    InBlock.gif
            Thread.Sleep(1000);
    InBlock.gif        
    // If you start a thread yourself, you can wait for it to end
    InBlock.gif        
    // by calling Thread.Join.  This option is not available with 
    InBlock.gif        
    // thread pool threads.
    ExpandedSubBlockEnd.gif
        }

    InBlock.gif   
    InBlock.gif    
    // The callback method executes when the registered wait times out,
    InBlock.gif    
    // or when the WaitHandle (in this case AutoResetEvent) is signaled.
    InBlock.gif    
    // WaitProc unregisters the WaitHandle the first time the event is 
    InBlock.gif    
    // signaled.
    ExpandedSubBlockStart.gifContractedSubBlock.gif
        public static void WaitProc(object state, bool timedOut) dot.gif{
    InBlock.gif        
    // The state object must be cast to the correct type, because the
    InBlock.gif        
    // signature of the WaitOrTimerCallback delegate specifies type
    InBlock.gif        
    // Object.
    InBlock.gif
            TaskInfo ti = (TaskInfo) state;
    InBlock.gif
    InBlock.gif        
    string cause = "TIMED OUT";
    ExpandedSubBlockStart.gifContractedSubBlock.gif        
    if (!timedOut) dot.gif{
    InBlock.gif            cause 
    = "SIGNALED";
    InBlock.gif            
    // If the callback method executes because the WaitHandle is
    InBlock.gif            
    // signaled, stop future execution of the callback method
    InBlock.gif            
    // by unregistering the WaitHandle.
    InBlock.gif
                if (ti.Handle != null)
    InBlock.gif                ti.Handle.Unregister(
    null);
    ExpandedSubBlockEnd.gif        }
     
    InBlock.gif
    InBlock.gif        Console.WriteLine(
    "WaitProc( {0} ) executes on thread {1}; cause = {2}.",
    InBlock.gif            ti.OtherInfo, 
    InBlock.gif            Thread.CurrentThread.GetHashCode().ToString(), 
    InBlock.gif            cause
    InBlock.gif        );
    ExpandedSubBlockEnd.gif    }

    ExpandedBlockEnd.gif}

    None.gif

转载于:https://www.cnblogs.com/shipfi/archive/2005/09/02/228770.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值