转自:http://blog.sina.com.cn/s/blog_494305f30100ryw7.html

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

CLR教程之线程池的产生

当 CLR 初始化时,其线程池中不含有线程。当应用程序要创建线程来执行任务时,该应用程序应请求线程池线程来执行任务。线程池知道后将创建一个初始线程。

该新线程经历的初始化和其他线程一样;但是任务完成后,该线程不会自行销毁。相反,它会以挂起状态返回线程池。如果应用程序再次向线程池发出请求,那么这个挂起的线程将激活并执行任务,而不会创建新线程。这节约了很多开销。

只要线程池中应用程序任务的排队速度低于一个线程处理每项任务的速度,那么就可以反复重用同一线程,从而在应用程序生存期内节约大量开销。

如果线程池中应用程序任务排队的速度超过一个线程处理任务的速度,则线程池将创建额外的线程。当然,创建新线程确实会产生额外开销,但应用程序在其生存期中很可能只请求几个线程来处理交给它的所有任务。因此,总体来说,通过使用线程池可以提高应用程序的性能。

线程池的一个绝妙特性是:它是启发式的。如果您的应用程序需要执行很多任务,那么线程池将创建更多的线程。如果您的应用程序的工作负载逐渐减少,那么线程池线程将自行终止。线程池的算法确保它仅包含置于其上的工作负荷所需要的线程数!

因此,希望您现在已理解了线程池的基本概念,并明白了它所能提供的性能优势。现在我将给出一些代码来说明如何使用线程池。首先,您应该知道线程池可以提供四种功能:

1> 异步调用方法

2> 以一定的时间间隔调用方法

3> 当单个内核对象得到信号通知时调用方法

4> 当异步 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);

代码示例:

  1. using System;
  2. using System.Threading;
  3. public class Test
  4. {
  5. // 存放要计算的数值的字段
  6. static double dRes1 = -1;
  7. static double dRes2 = -1;
  8. public static void Main()
  9. {
  10. // 获取线程池的最大线程数和维护的最小空闲线程数
  11. int maxThreadNum, portThreadNum;
  12. int minThreadNum;
  13. ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum);
  14. ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum);
  15. Console.WriteLine("最大线程数:{0}", maxThreadNum);
  16. Console.WriteLine("最小空闲线程数:{0}", minThreadNum);
  17. // 函数变量值
  18. int x = 15600;
  19. // 启动第一个任务:计算x的8次方
  20. Console.WriteLine("启动第一个任务:计算{0}的8次方。", x);
  21. ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc1), x);
  22. // 启动第二个任务:计算x的8次方根
  23. Console.WriteLine("启动第二个任务:计算{0}的8次方根。", x);
  24. ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc2), x);
  25. // 等待,直到两个数值都完成计算
  26. while (dRes1 == -1 || dRes2 == -1) ;
  27. // 打印计算结果
  28. Console.WriteLine("{0}的8次方和8次方根分别为{1}, {2}", x, dRes1, dRes2);
  29. }
  30. // 启动第一个任务:计算x的8次方
  31. static void TaskProc1(object o)
  32. {
  33. dRes1 = Math.Pow(Convert.ToDouble(o), 8);
  34. }
  35. // 启动第二个任务:计算x的8次方根
  36. static void TaskProc2(object o)
  37. {
  38. dRes2 = Math.Pow(Convert.ToDouble(o), 1.0 / 8.0);
  39. }
  40. }

功能 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 方法

作用:注册一个等待 WaitHandle 的委托

函数原型:

public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,
WaitOrTimerCallback callBack,
Object state,
int millisecondsTimeOutInterval,
bool executeOnlyOnce
)

参数:

1> waitObject 标识出您想要线程池等待的内核对象。

由于该参数是抽象基类 System.Threading.WaitHandle,因此您可以指定从该基类派生出来的任何类。

特别地,您可以将一个引用传递给 AutoResetEvent、ManualResetEvent 或 Mutex object

2> callBack 标识出您想要线程池线程调用的方法。

注意:回调方法必须与 System.Threading.WaitOrTimerCallback 委托类型相匹配,其定义如下列代码行所示:

public delegate void WaitOrTimerCallback(Object state, Boolean timedOut);

3> state 允许您指定应传递给回调方法的某些状态数据,如果没有特别的状态数据要传递,则传递 null;

4> milliseconds 允许您告诉线程池内核对象得到信号通知前应该等待的时间。这里通常传递 -1,以表示阻塞等待。

5> executeOnlyOnce指出回调函数为执行多次还是一次

如果取值为真,那么线程池线程将仅执行回调方法一次;

如果取值为假,那么线程池线程将在内核对象每次得到信号通知时执行回调方法。这对 AutoResetEvent 对象非常有用。

返回值:

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

public Boolean Unregister(WaitHandle waitObject);

waitObject 参数表明当执行完队列中的所有工作项后,您想如何得到信号通知。

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

小结

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

线程池(C#)的更多相关文章

  1. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  2. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  3. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  4. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  5. NGINX引入线程池 性能提升9倍

    1. 引言 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为 ...

  6. Java线程池解析

    Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...

  7. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  8. Android线程管理之ThreadPoolExecutor自定义线程池

    前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...

  9. -Android -线程池 批量上传图片 -附php接收代码

    (出处:http://www.cnblogs.com/linguanh/) 目录: 1,前序 2,类特点 3,用法 4,java代码 5,php代码 1,前序 还是源于重构,看着之前为赶时间写着的碎片 ...

  10. C#多线程--线程池(ThreadPool)

    先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...

随机推荐

  1. C语言 段位

    C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为"位段"或"位域" 在结构体中位段的定义格式为: unsignede < ...

  2. Go Mobile 例子 basic 源码分析

    OpenGL ES(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集,针对手机.PDA和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广, ...

  3. Codeforces 132E Bits of merry old England 【最小费用最大流】

    题意: 让你输出长度为n的某个序列,然后给你m个变量. 每次给某个数赋值的代价是 假设赋值a=7那么代价是3,因为7的二进制位中有3个1. 要求最后总代价最小. 输出总共要进行操作的次数,和最小代价. ...

  4. JSP SQL注入

    Login.JSP <%@ page language="java" import="java.util.*" pageEncoding="UT ...

  5. JAVA集合学习

    JAVA中有几种常用的集合类.分别是List,Set,Map等 提示:Eclipse中自动导入包的快捷键  Ctrl+Shift+O 一.List类 父接口:该类是Collection集合接口的子接口 ...

  6. GitHub指南

    1.创建新仓库 #创建新文件夹,打开,然后执行 git init #以创建新的 git 仓库. 2.检出仓库 #执行如下命令以创建一个本地仓库的克隆版本: git clone /path/to/rep ...

  7. Eclipse 安装反编译插件jadclipse

    下载jadClipse地址: 链接: http://pan.baidu.com/s/1kTN4TPd  提取码: 3fvd 将net.sf.jadclipse_3.3.0.jar拷贝到eclipse的 ...

  8. 洛谷P1529 回家 Bessie Come Home

    P1529 回家 Bessie Come Home 题目描述 现在是晚餐时间,而母牛们在外面分散的牧场中. 农民约翰按响了电铃,所以她们开始向谷仓走去. 你的工作是要指出哪只母牛会最先到达谷仓(在给出 ...

  9. Vmware ESX5i 环境下部署Windows Storage Server 2008 R2

    ESX5i 环境下部署Windows Storage Server 2008 R2       Windows Storage Server 2008 这款产品微软早已发布,WSS2008是基于Win ...

  10. jquery的add()方法扩大选择返回

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...