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 ...
随机推荐
- solr搜索之搜索精度问题我已经尽力了!!!
solr搞了好久了,没啥进展,没啥大的突破,但是我真的尽力了! solr7可能是把默认搜索方式去掉了,如下: 在solr7里找了半天以及各种查资料也没发现这个默认搜索方式,后来想,可能是被edisma ...
- probably another instance of uWSGI is running on the same address
probably another instance of uWSGI is running on the same address 可以用命令杀掉这个端口在重启: /tcp
- 2个版本并存的python使用新的版本安装django的方法
2个版本并存的python使用新的版本安装django的方法 默认是使用 pip install django 最新版的django会提示 要求python版本3.4以上,系统默认的版本是2.7.5 ...
- 好记性不如烂笔头-linux学习笔记6keepalived实现主备操作
Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工 ...
- SQL Server数据库partition by 与ROW_NUMBER()函数使用详解[转]
关于SQL的partition by 字段的一些用法心得 先看例子: if object_id('TESTDB') is not null drop table TESTDB create table ...
- Rhythmk 学习 Hibernate 04 - Hibernate 辅助工具 之 JBoos Tool
1.安装JBoos Tool Help -> Install new Software 然后添加: http://download.jboss.org/jbosstools/updates/de ...
- leetcode257
/** * Definition for a binary tree node. * public class TreeNode { * public int val; * public TreeNo ...
- DBNavigator中把insert变为append
procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);begin if Button = nbIns ...
- [ML]熵、KL散度、信息增益、互信息-学习笔记
[ML]熵.KL散度.信息增益.互信息-学习笔记 https://segmentfault.com/a/1190000000641079
- varchar和Nvarchar区别(转)
Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示 如果还为了这个纠结,就直接看看后面的解说,做决定吧. 一般如果用到中文或者其 ...