c# 多线程线程池基础
线程池的作用
在上一篇中我们了解了创建和销毁线程是一个昂贵的操作,要耗费大量的时间,太多的线程会浪费内存资源,当线程数量操作计算机CPU的数量后操作系统必须调度可运行的线程并执行上下文切换,所有太多的线程还会影响性能,那么有没有办法让线程可以重复使用了,让线程干完活之后不用销毁,把它放在一个容器中, 等待下次有任务的时候在从容器中取出来就行了,这样就避免了创建和销毁所带来的性能损耗,所有线程池的作用总结起来就是:因为创建一个线程的代价较高,因此我们使用线程池设法复用线程。
线程基础
每个CLR 拥有一个线程池,这个线程池由CLR控制的APPDomain 共享。在线程池内部,它自己维护着一个操作请求队列,应用程序需要执行某个任务时,就需要调用线程池的一个方法(通常是QueueUserWorkItem 方法)将任务添加到线程池工作项中,线程池就会将任务分派给一个线程池线程处理,如果线程池中没有线程,就会创建一个线程来处理这个任务。当任务执行完成以后,这个线程会回到线程池中处于空闲状态,等待下一个执行任务。由于线程不会销毁,所以使用线程池线程在执行任务的速度上会更快。
如果线程池中的任务过多超过了现有线程的处理能力时,线程池就会根据需要在创建更多的线程。由于每个线程都要占用一定的内存资源,所以当线程池空闲线程(长时间不执行任务的线程)过多时,线程池中线程会自动醒来销毁多余的空闲线程,以减少资源的使用。
在线程池内部,所有线程都是后台线程并且调度优先级都为普通(ThreadPriority.Normal),这些线程分为工作者或I/O线程,当线程池线程执行的任务是一个复杂的计算任务时,使用的就是工作者线程。如果执行的任务与I/O相关,就会使用I/O线程。
如何使用线程池
ThreadPool 类是一个静态类型类,使用ThreadPool 类执行异步时通常调用ThreadPool 的 QueueUserWorkItem 方法,这个方法有一个重载版本,如下
public static bool QueueUserWorkItem(WaitCallback callBack)
{
StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
return ThreadPool.QueueUserWorkItemHelper(callBack, null, ref stackCrawlMark, true);
}
public static bool QueueUserWorkItem(WaitCallback callBack, object state)
{
StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
return ThreadPool.QueueUserWorkItemHelper(callBack, state, ref stackCrawlMark, true);
}
QueueUserWorkItem 方法接受一个WaitCallback 类型的委托作为回调方法以及可以选择传递一个线程池线程执行回调方法时所需要的数据对象。
WaitCallback 委托类型的定义如下:
namespace System.Threading
{
[__DynamicallyInvokable, ComVisible(true)]
public delegate void WaitCallback(object state);
}
线程池的QueueUserWorkItem方法在调用以后会立即返回,所传递的回调方法会有以后线程池线程执行。使用线程池线程执行异步任务代码如下:
static void Main(string[] args)
{ Console.WriteLine("主线程开始执行任务,线程ID:{0}",Thread.CurrentThread.ManagedThreadId);
//使用 ThreadPool.QueueUserWorkItem 方法将一个异步任务添加到线程池任务队列中,
//可以为线程池线程执行方法时传递一个数据对象,
//如果不需要传递数据可以使用QueueUserWorkItem只有WaitCallback一个参数类型的版本,
//或传递null
ThreadPool.QueueUserWorkItem(ThreadMethod,null);
Console.WriteLine("主线程执行其他任务。线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
//使调用线程睡眠2000毫秒,等待线程池线程执行完成。
Thread.Sleep(2000); Console.WriteLine("主线程继续执行任务。线程ID:{0}", Thread.CurrentThread.ManagedThreadId); }
public static void ThreadMethod(object state)
{
Console.WriteLine("我是由线程池创建的线程,线程ID:{0}",Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
Console.WriteLine(i);
}
}
输出
主线程开始执行任务,线程ID:1
主线程执行其他任务。线程ID:1
我是由线程池创建的线程,线程ID:3
0
1
2
3
4
5
6
7
8
主线程继续执行任务。线程ID:1
请按任意键继续. . .
线程池对线程的管理
线程池中的线程是由工作者线程和I/O线程组成的, CLR允许开发人员设置线程池需要创建的最大线程数量,但实践证明,线程池永远都不应该为池中的线程数设置上限,因为可能发生饥饿或死锁,假定队列中有1000个工作项,但这些工作项全都因为一个事件而阻塞,等第1001个工作项发出信号才能解除阻塞,如果设置了最大1000个线程,第1001个工作项就不会执行,所有1000个线程都会一直阻塞,最终用户将被终止应用程序,并丢失他们的所有未保存的工作。
//设置可以同时处于活动状态的线程池的请求数目。
// 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
public static bool SetMaxThreads(int workerThreads, int completionPortThreads);
// 发出新的请求时,在切换到管理线程创建和销毁的算法之前设置线程池按需创建的线程的最小数量。
public static bool SetMinThreads(int workerThreads, int completionPortThreads);
//检索可以同时处于活动状态的线程池请求的数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
public static void GetMaxThreads(out int workerThreads, out int completionPortThreads);
//发出新的请求时,在切换到管理线程创建和销毁的算法之前检索线程池按需创建的线程的最小数量。
public static void GetMinThreads(out int workerThreads, out int completionPortThreads);
//获得最大线程池线程数和当前运行线程数之间的差值。
public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads);
虽然ThreadPool类提供了上诉方法,但建议不要调用上诉中的方法,限制线程池的线程数,一般都只造成应用程序的性能变的更差,而不会更好,使用方法
static void Main(string[] args)
{
int worker, io;
//获得线程池默认最大线程数
ThreadPool.GetMaxThreads(out worker, out io);
Console.WriteLine("1、CLR线程池默认最大线程数据,工作者线程数:{0},IO线程数:{1}", worker, io);
//设置线程池最大线程数
ThreadPool.SetMaxThreads(100, 100);
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(2000);
Console.WriteLine("4、线程池线程开始执行异步任务。线程ID:{0}", Thread.CurrentThread.ManagedThreadId); });
Console.WriteLine("2、自定义设置线程池默认最大线程数据后,工作者线程数:{0},IO线程数:{1}", worker, io);
//获得最大线程池线程数和当前运行线程数之间的差值。
ThreadPool.GetAvailableThreads(out worker, out io);
Console.WriteLine("3、获得最大线程池线程数和当前运行线程数之间的差值,工作者线程:{0},IO线程:{1}", worker, io);
Console.Read(); } 1、CLR线程池默认最大线程数据,工作者线程数:2047,IO线程数:1000
2、自定义设置线程池默认最大线程数据后,工作者线程数:2047,IO线程数:1000
3、获得最大线程池线程数和当前运行线程数之间的差值,工作者线程:99,IO线程:100
4、线程池线程开始执行异步任务。线程ID:3
c# 多线程线程池基础的更多相关文章
- C#多线程--线程池(ThreadPool)
先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...
- linux C 多线程/线程池编程 同步实例
在多线程.线程池编程中经常会遇到同步的问题. 1.创建线程 函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, ...
- Java 多线程(五)—— 线程池基础 之 FutureTask源码解析
FutureTask是一个支持取消行为的异步任务执行器.该类实现了Future接口的方法. 如: 取消任务执行 查询任务是否执行完成 获取任务执行结果(”get“任务必须得执行完成才能获取结果,否则会 ...
- Java 基础 多线程和线程池基础
一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...
- Java线程和多线程(十二)——线程池基础
Java 线程池管理多个工作线程,其中包含了一个队列,包含着所有等待被执行的任务.开发者可以通过使用ThreadPoolExecutor来在Java中创建线程池. 线程池是Java中多线程的一个重要概 ...
- java基础-多线程线程池
线程池 * 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互.而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池.线程池里的每一个线程代 ...
- JAVA基础知识之多线程——线程池
线程池概念 操作系统或者JVM创建一个线程以及销毁一个线程都需要消耗CPU资源,如果创建或者销毁线程的消耗源远远小于执行一个线程的消耗,则可以忽略不计,但是基本相等或者大于执行线程的消耗,而且需要创建 ...
- C#线程池基础
池(Pool)是一个很常见的提高性能的方式.比如线程池连接池等,之所以有这些池是因 为线程和数据库连接的创建和关闭是一种比较昂贵的行为.对于这种昂贵的资源我们往往会考虑在一个池容器中放置一些资源,在用 ...
- delphi 线程池基础 TSimplePool
1. TSimpleThread 2. TSimpleList 3. 以1,2构成 TSimplePool 用法 先定义: TDoSomeThingThread=class(TSimpleThread ...
随机推荐
- 超文本传输协议http详解
HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1.0的第 ...
- centos7.3部署django用uwsgi和nginx[亲测可用]
现在nginx nginx version: nginx/1.10.2 uwsgi 2.0.17 django2.0.5 都已经完成完毕,那么开始 uwsgi 配置 uwsgi支持ini.xml等多种 ...
- 关于memcache分布式一致性hash
consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在 cache 系统中应用越来越广泛: 1 ...
- requests接口测试——身份认证
当调用者访问接口时需要进行用户认证(用户名密码的登录),只有通过了认证才允许调用者访问接口. 1.基本身份认证 许多要求身份认证的web服务都接受HTTP Basic Auth.这是最简单的一种身份认 ...
- 归纳整理Linux下C语言常用的库函数----字符串转换、字符测试、及内存控制
在没有IDE的时候,记住一些常用的库函数的函数名.参数.基本用法及注意事项是很有必要的. 参照Linux_C_HS.chm的目录,我大致将常用的函数分为一下几类: 1. 内存及字符串控制及操作 2. ...
- clr相关名词
程序集:一个或多个类型定义文件和资源文件的集合 Native Code(本机代码): 已被编译为特定于处理器的机器码的代码. 本地代码(native code)是计算机编程(代码),编译用来运行一个特 ...
- readlink 获取当前进程对应proc/self/exe
[readlink 获取当前进程对应proc/self/exe] shell中 readlink /proc/self/exe READLINK(2)NAME readlink - re ...
- dock panel
http://www.cnblogs.com/masterfy/archive/2009/06/02/1494593.html http://www.cnblogs.com/wuhuacong/p/3 ...
- yarn 完美替代 npm
众所周知,npm是nodejs默认的包管理工具,我们通过npm可以下载安装或者发布包,但是npm其实存在着很多小问题,比如安装速度慢.每次都要在线重新安装等,而yarn也正是为了解决npm当前存在的问 ...
- [Selenium]怎样验证页面是否有无变化
验证方法:将两次的Dom结构进行对比 String beforeStr = (String) SeleniumUtil.getInnerHTML(page.getDriver(), page.getD ...