`

c#线程池详解

阅读更多

 

在这里你可以学到Microsoft研究CLR实现线程池的原理机制,从而更灵活的处理CLR在实际代码应中线程池的问题,下面我们来看看吧。

CLR教程之线程池的产生

当 CLR 初始化时,其线程池中不含有线程。当应用程序要创建线程来执行任务时,该应用程序应请求线程池线程来执行任务。线程池知道后将创建一个初始线程。该新线程经历的初始化和其他线程一样;但是任务完成后,该线程不会自行销毁。相反,它会以挂起状态返回线程池。如果应用程序再次向线程池发出请求,那么这个挂起的线程将激活并执行任务,而不会创建新线程。这节约了很多开销。只要线程池中应用程序任务的排队速度低于一个线程处理每项任务的速度,那么就可以反复重用同一线程,从而在应用程序生存期内节约大量开销。

那么,如果线程池中应用程序任务排队的速度超过一个线程处理任务的速度,则线程池将创建额外的线程。当然,创建新线程确实会产生额外开销,但应用程序在其生存期中很可能只请求几个线程来处理交给它的所有任务。因此,总体来说,通过使用线程池可以提高应用程序的性能。线程池的一个绝妙特性是:它是启发式的。如果您的应用程序需要执行很多任务,那么线程池将创建更多的线程。如果您的应用程序的工作负载逐渐减少,那么线程池线程将自行终止。线程池的算法确保它仅包含置于其上的工作负荷所需要的线程数!

因此,希望您现在已理解了线程池的基本概念,并明白了它所能提供的性能优势。现在我将给出一些代码来说明如何使用线程池。首先,您应该知道线程池可以提供四种功能: 
◆异步调用方法 
◆以一定的时间间隔调用方法 
◆当单个内核对象得到信号通知时调用方法 
◆当异步 I/O 请求结束时调用方法

前三种功能非常有用,我将在本专栏中加以说明。而应用程序开发人员很少使用第四种功能,因此在此我将不做说明;有可能在将来的专栏中讲到。

功能 1:CLR线程池教程之异步调用方法

在您的应用程序中,如果有创建新线程来执行任务的代码,那么我建议您用命令线程池执行该任务的新代码来替换它。事实上,您通常会发现,让线程池执行任务比让一个新的专用线程来执行任务更容易。要排队线程池任务,您可以使用 System.Threading 命名空间中定义的 ThreadPool 类。ThreadPool 类只提供静态方法,且不能构造它的实例。要让线程池线程异步调用方法,您的代码必须调用一个 ThreadPool 的重载 QueueUserWorkItem 方法,如下所示:

public static Boolean QueueUserWorkItem(WaitCallback wc, Object state);  public static Boolean QueueUserWorkItem(WaitCallback wc);  这些方法将“工作项”(和可选状态数据)排队到线程池的线程中,并立即返回。工作项只是一种方法(由 wc 参数标识),它被调用并传递给单个参数,即状态(状态数据)。没有状态参数的 QueueUserWorkItem 版本将 null 传递给回调方法。最后,池中的某些线程将调用您的方法来处理该工作项。您编写的回调方法必须与 System.Threading.WaitCallback 委托类型相匹配,其定义如下:

public delegate void WaitCallback(Object state);

代码示例:

01 using System;
02 using System.Threading;
03   
04 public class Test
05 {
06     // 存放要计算的数值的字段
07     static double number1 = -1;
08     static double number2 = -1;
09   
10     public static void Main()
11     {
12         // 获取线程池的最大线程数和维护的最小空闲线程数
13         int maxThreadNum, portThreadNum;
14         int minThreadNum;
15         ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum);
16         ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum);
17         Console.WriteLine("最大线程数:{0}", maxThreadNum);
18         Console.WriteLine("最小空闲线程数:{0}", minThreadNum);
19   
20         // 函数变量值
21         int x = 15600;
22   
23         // 启动第一个任务:计算x的8次方
24         Console.WriteLine("启动第一个任务:计算{0}的8次方。", x);
25         ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc1), x);
26   
27         // 启动第二个任务:计算x的8次方根
28         Console.WriteLine("启动第二个任务:计算{0}的8次方根。", x);
29         ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc2), x);
30   
31         // 等待,直到两个数值都完成计算
32         while (number1 == -1 || number2 == -1) ;
33   
34         // 打印计算结果
35         Console.WriteLine("y({0}) = {1}", x, number1 + number2);
36   
37     }
38   
39     // 启动第一个任务:计算x的8次方
40     static void TaskProc1(object o)
41     {
42         number1 = Math.Pow(Convert.ToDouble(o), 8);
43     }
44   
45     // 启动第二个任务:计算x的8次方根
46     static void TaskProc2(object o)
47     {
48         number2 = Math.Pow(Convert.ToDouble(o), 1.0 / 8.0);
49     }
50   
51 }

功能 2:CLR线程池教程之以一定的时间间隔调用方法

如果您的应用程序需要在某一时间执行某项任务,或者您的应用程序需要定期执行某些方法,那么使用线程池将是您的最佳选择。System.Threading 命名空间定义 Timer 类。当您构造 Timer 类的实例时,您是在告诉线程池您想在将来的某个特定时间回调自己的某个方法。Timer 类有四种构造函数:

public Timer(TimerCallback callback, Object state,  Int32 dueTime, Int32 period);  public Timer(TimerCallback callback, Object state,  UInt32 dueTime, UInt32 period);  public Timer(TimerCallback callback, Object state,  Int64 dueTime, Int64 period);  public Timer(TimerCallback callback, Object state,  Timespan dueTime, TimeSpan period);   所有这四种构造函数构造完全相同的 Timer 对象。回调参数标识您想由线程池线程回调的方法。当然,您编写的回调方法必须与 System.Threading.TimerCallback 委托类型相匹配,其定义如下:

public delegate void TimerCallback(Object state); 构造 Timer 对象后,线程池知道要做什么,并自动为您监视时间。然而,Timer 类还提供了几种其他的方法,允许您与线程池进行通信,以便更改什么时候(或者是否)应当回调方法。具体地说,Timer 类提供了几种 Change 和 Dispose 方法:

public Boolean Change(Int32    dueTime, Int32    period);  public Boolean Change(UInt32   dueTime, UInt32   period);  public Boolean Change(Int64    dueTime, Int64    period);  public Boolean Change(TimeSpan dueTime, TimeSpan period);   public Boolean Dispose();  public Boolean Dispose(WaitHandle notifyObject);  Change 方法允许您更改 Timer 对象的 dueTime 和 period。Dispose 方法允许您在所有挂起的回调已经完成的时候,完全取消回调,并可选地用信号通知由 notifyObject 参数标识的内核对象。

功能 3:CLR线程池教程之当单个内核对象得到信号通知时调用方法

要让线程池线程在内核对象得到信号通知时调用您的回调方法,您可以再次利用 System.Threading.ThreadPool 类中定义的一些静态方法。要让线程池线程在内核对象得到信号通知时调用方法,您的代码必须调用一个重载的 RegisterWaitHandle 方法,当您调用这些方法之一时,h 参数标识出您想要线程池等待的内核对象。由于该参数是抽象基类 System.Threading.WaitHandle,因此您可以指定从该基类派生出来的任何类。特别地,您可以将一个引用传递给 AutoResetEvent、ManualResetEvent 或 Mutex object。第二个参数 callback 标识出您想要线程池线程调用的方法。您实现的回调方法必须与 System.Threading.WaitOrTimerCallback 委托类型相匹配,其定义如下列代码行所示:

public delegate void WaitOrTimerCallback(Object state,  Boolean timedOut); 第三个参数 state 允许您指定应传递给回调方法的某些状态数据,如果没有特别的状态数据要传递,则传递 null。第四个参数 milliseconds 允许您告诉线程池内核对象得到信号通知前应该等待的时间。这里通常传递 -1,以表示无限超时。如果最后一个参数 executeOnlyOnce 为真,那么线程池线程将仅执行回调方法一次。但是,如果 executeOnlyOnce 为假,那么线程池线程将在内核对象每次得到信号通知时执行回调方法。这对 AutoResetEvent 对象非常有用。

在前面所示的原型中,您会注意到 RegisterWaitForSingleObject 方法返回一个 RegisteredWaitHandle 对象。该对象确定线程池在等待的内核对象。如果由于某种原因,您的应用程序要告诉线程池停止监视已注册的等待句柄,那么您的应用程序就可以调用 RegisteredWaitHandle 的 Unregister 方法:

public Boolean Unregister(WaitHandle waitObject); waitObject 参数表明当执行完队列中的所有工作项后,您想如何得到信号通知。如果不想得到信号通知,那么您应将 null 传递给该参数。如果您将一个有效引用传递给 WaitHandle-derived 对象,那么线程池会在已注册等待句柄的所有挂起工作项执行完后,通知该对象。

小结

在本专栏中,我讲述了对线程池的需要,说明了如何利用 CLR 线程池提供的各种功能。现在您应该明白线程池为您的开发所带来的价值,它可以提高您的应用程序的性能,并简化您的代码。

 

本文自转摘

分享到:
评论

相关推荐

    C# CLR原理与线程池详解

    ### C# CLR原理与线程池详解 #### 一、CLR与.NET框架 **CLR**(Common Language Runtime,公共语言运行时)是.NET框架的核心组件之一,它为.NET应用程序提供了执行环境,确保了代码的安全性、可靠性和兼容性。深入...

    c#线程池应用

    ### C#线程池应用详解 #### 一、引言 在现代软件开发中,多线程编程是一项不可或缺的技术,特别是在高性能和响应性要求较高的应用中。本文将深入探讨Microsoft .NET框架中线程池的概念及其应用。我们将讨论为什么...

    关于C#线程池的例子

    ### C#线程池应用详解 #### 一、引言 在现代软件开发中,特别是在网络编程领域,高效地利用系统资源是至关重要的。多线程技术为提高程序的并发性和响应性提供了强大的支持。本篇文章将通过一个具体的案例——使用...

    C#多线程ThreadPool线程池详解

    C#多线程ThreadPool线程池详解 C#多线程ThreadPool线程池是C#中的一种线程管理机制,用于管理和维护线程池中的线程。下面是对C#多线程ThreadPool线程池的详细介绍。 一、线程池的定义 线程池可以看做容纳线程的...

    csharp多线程示例(全)

    C#是一种广泛使用的编程语言,尤其在开发Windows应用程序和.NET框架中扮演着核心角色。多线程是C#中的一个重要特性,它允许程序同时执行多个任务,从而提高效率和响应速度。本示例集合旨在深入讲解C#多线程的概念,...

    C#线程详解,单线程,多线程,线程管理

    在C#编程中,线程是程序执行的基本单元,它使得多个任务可以在同一时间并行运行,极大地提高了程序的效率。本篇文章将深入探讨C#中的单线程、多线程以及线程管理,帮助你理解和掌握这一核心概念。 ### 1. 单线程 ...

    c#多线程的奥妙,多线程的概念STAThread教程+源代码,非常好的.

    此外,线程优先级、线程池、后台线程、异步编程模型(如Task和async/await)等也是C#多线程编程中的重要概念。线程池可以有效管理线程的生命周期,提高系统效率。后台线程不会阻止程序的退出,而异步编程模型可以...

    C#多线程详解

    ### C#多线程详解 #### 一、基础篇 **1. 怎样创建一个线程** 在C#中创建线程的方式有很多种,这里主要介绍三种常用的创建方式:使用`Thread`类、使用`Delegate.BeginInvoke`以及使用`ThreadPool....

    浅谈C#网络编程详解篇

    在C#中,可以利用Task或者async/await关键字实现异步处理,避免线程池过度膨胀: ```csharp async Task HandleClient(Socket clientSocket) { byte[] receiveBuffer = new byte[100]; await clientSocket....

    C#线程参考手册(初学者最佳入门书籍)

    《C#线程参考手册》是一本专为C#初学者设计的多线程编程指南。这本书深入浅出地介绍了C#中线程的概念、创建与管理,以及如何利用线程提升程序性能和效率。线程是现代软件开发中的重要组成部分,尤其是在多核处理器...

    c#广播实现

    可以使用Task或者线程池来实现这一目的。 在"局域网广播实现"的源码中,你可以看到这些基本步骤的实现,以及可能的扩展,如添加异常处理、数据解析、心跳机制等。通过对源码的学习和研究,你不仅可以掌握C#的UDP...

    c#开发技术大全pdf

    书中会讲解线程、线程同步、线程池、异步编程模型(如Task和async/await)等并发编程技术。 8. **内存管理**:了解C#中的内存管理有助于优化程序性能。书中会探讨对象生命周期、引用计数、垃圾回收机制,以及如何...

    C# MES_开源源代码

    【C# MES 开源源代码详解】 C# MES(Manufacturing Execution System,制造执行系统)是基于C#编程语言开发的一款开源软件,旨在为企业提供生产流程的实时监控与管理。MES系统通常位于ERP(企业资源规划)系统和...

    C#ThreadPool之QueueUserWorkItem使用案例详解.docx

    《C# ThreadPool中的QueueUserWorkItem使用详解》 在C#编程中,ThreadPool是一个非常重要的概念,它是一种线程管理机制,能够有效地管理和调度线程,以提高程序的执行效率。其中,ThreadPool类中的...

    C# IOCP完整代码和演示

    3. **线程池处理**:通常我们会使用线程池来处理来自IOCP的完成通知。当`GetQueuedCompletionStatus`返回时,表明一个I/O操作已完成,我们可以处理结果,如读取数据、写入响应等。 4. **错误处理**:DEMO还会包含对...

    C#_多线程技术详解

    【C#多线程技术详解】 C#编程语言在处理多线程方面提供了强大的支持,使得开发者能够构建高效且响应迅速的应用程序。本文将详细解析C#中的多线程技术,涵盖线程概述、.NET对多线程的支持、如何创建和管理线程以及...

    Halcon c#源代码

    《Halcon C#源代码详解:开启机器视觉的编程之旅》 Halcon,作为全球知名的机器视觉软件,为开发者提供了强大的图像处理与模式识别功能。本文将深入探讨Halcon C#源代码,揭示如何在C#环境中高效地调用Halcon的函数...

Global site tag (gtag.js) - Google Analytics