转自: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. [SQL]reName存储过程

    exec sp_helptext aa--应用sp_helptext查看存储过程的定义文本 exec sp_depends aa --通过sp_depends查看存储过程的相关性 exec sp_he ...

  2. 【原】linux系统运维工具必备

    操作系统:CentOS※,Ubuntu 网站服务:apache,nginx※,tomcat,tengine 开发语言:php,python※,shell※ 数据库 :Mysql※ 代理相关:lvs,k ...

  3. Oracle 11gR2 Database UNDO表空间使用率居高不下-转载

    客户的数据库是Oracle Database 11.2.0.3.0 for AIX 6.1 64bit的单机数据库.客户查询DBA_FREE_SPACE发现UNDO表空间的使用率高达98%以上.客户的 ...

  4. 将assembly包添加到自己的maven仓库

    mvn install:install-file -DgroupId=com.asiainfo -DartifactId=spark-assembly -Dversion=1.2.0 -Dpackag ...

  5. ASP.NET MVC 4 部署到 Windows Azure 如何轉換時區設定

    由於公司慢慢地開始將新的專案都移往 Windows Azure 雲端平台做網站代管,漸漸地也開始遇到一些小問題,這些問題在還沒上雲端之前通常不會發生,像我們這次遇到的問題就跟顯示時間有關.由於 Win ...

  6. POJ3264RMQ

    http://poj.org/problem?id=3264 #include<stdio.h> #include<string.h> #include<algorith ...

  7. jmeter随笔(23)--在csv中维护变量参数

    点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...

  8. QQ音乐无损歌曲接口api

    1.打开QQ音乐官网 y.qq.com 2.选择你要的歌曲,进入后查看网址,以虎口脱险这个歌曲为例:http://y.qq.com/#type=song&mid=000Ib8E71sUNi7 ...

  9. Windows Azure - App Services

    1. 需要了解的概念:App Service Plan, Resource Group 2. Create an ASP.NET web app in Azure App Services 3. Cr ...

  10. 第2章 linux文件系统

    1.用户和用户组管理 1.1 用户管理常用命令 1.用户账号添加命令useradd或adduser 命令格式:useradd [option] [username] 其中[option]为userad ...