CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。
线程池初始化时是没有线程的,线程池里的线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。
这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。
通过CLR线程池所建立的线程总是默认为后台线程,优先级数为ThreadPriority.Normal。
CLR线程池分为工作者线程(workerThreads)与I/O线程(completionPortThreads)两种:
- 工作者线程是主要用作管理CLR内部对象的运作,通常用于计算密集的任务。
- I/O(Input/Output)线程主要用于与外部系统交互信息,如输入输出,CPU仅需在任务开始的时候,将任务的参数传递给设备,然后启动硬件设备即可。等任务完成的时候,CPU收到一个通知,一般来说是一个硬件的中断信号,此时CPU继续后继的处理工作。在处理过程中,CPU是不必完全参与处理过程的,如果正在运行的线程不交出CPU的控制权,那么线程也只能处于等待状态,即使操作系统将当前的CPU调度给其他线程,此时线程所占用的空间还是被占用,而并没有CPU处理这个线程,可能出现线程资源浪费的问题。如果这是一个网络服务程序,每一个网络连接都使用一个线程管理,可能出现大量线程都在等待网络通信,随着网络连接的不断增加,处于等待状态的线程将会很消耗尽所有的内存资源。可以考虑使用线程池解决这个问题。
线程池的最大值一般默认为1000、2000。当大于此数目的请求时,将保持排队状态,直到线程池里有线程可用。
使用CLR线程池的工作者线程一般有两种方式:
- 通过ThreadPool.QueueUserWorkItem()方法;
- 通过委托;
要注意,不论是通过ThreadPool.QueueUserWorkItem()还是委托,调用的都是线程池里的线程。
通过以下两个方法可以读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。
- ThreadPool.GetMax(out in workerThreads,out int completionPortThreads);
- ThreadPool.SetMax(int workerThreads,int completionPortThreads);
若想测试线程池中有多少线程正在投入使用,可以通过ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。
方法 | 说明 |
GetAvailableThreads | 剩余空闲线程数 |
GetMaxThreads | 最多可用线程数,所有大于此数目的请求将保持排队状态,直到线程池线程变为可用 |
GetMinThreads | 检索线程池在新请求预测中维护的空闲线程数 |
QueueUserWorkItem | 启动线程池里得一个线程(队列的方式,如线程池暂时没空闲线程,则进入队列排队) |
SetMaxThreads | 设置线程池中的最大线程数 |
SetMinThreads | 设置线程池最少需要保留的线程数 |
我们可以使用线程池来解决上面的大部分问题,跟使用单个线程相比,使用线程池有如下优点:
1、缩短应用程序的响应时间。因为在线程池中有线程的线程处于等待分配任务状态(只要没有超过线程池的最大上限),无需创建线程。
2、不必管理和维护生存周期短暂的线程,不用在创建时为其分配资源,在其执行完任务之后释放资源。
3、线程池会根据当前系统特点对池内的线程进行优化处理。
总之使用线程池的作用就是减少创建和销毁线程的系统开销。在.NET中有一个线程的类ThreadPool,它提供了线程池的管理。
ThreadPool是一个静态类,它没有构造函数,对外提供的函数也全部是静态的。其中有一个QueueUserWorkItem方法,它有两种重载形式,如下:
public static bool QueueUserWorkItem(WaitCallback callBack):将方法排入队列以便执行。此方法在有线程池线程变得可用时执行。
public static bool QueueUserWorkItem(WaitCallback callBack,Object state):将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。
QueueUserWorkItem方法中使用的的WaitCallback参数表示一个delegate,它的声明如下:
public delegate void WaitCallback(Object state)
如果需要传递任务信息可以利用WaitCallback中的state参数,类似于ParameterizedThreadStart委托。
下面是一个ThreadPool的例子,代码如下:


using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Threading;namespace ConsoleApp1 {class ThreadPoolDemo{public ThreadPoolDemo(){}public void Work(){ThreadPool.QueueUserWorkItem(new WaitCallback(CountProcess));ThreadPool.QueueUserWorkItem(new WaitCallback(GetEnvironmentVariables));}/// <summary> /// 统计当前正在运行的系统进程信息 /// </summary> /// <param name="state"></param> private void CountProcess(object state){Process[] processes = Process.GetProcesses();foreach (Process p in processes){try{Console.WriteLine("进程信息:Id:{0},ProcessName:{1},StartTime:{2}", p.Id, p.ProcessName, p.StartTime);}catch (Win32Exception e){Console.WriteLine("ProcessName:{0}", p.ProcessName);}finally{}}Console.WriteLine("获取进程信息完毕。");}/// <summary> /// 获取当前机器系统变量设置 /// </summary> /// <param name="state"></param> public void GetEnvironmentVariables(object state){IDictionary list = System.Environment.GetEnvironmentVariables();foreach (DictionaryEntry item in list){Console.WriteLine("系统变量信息:key={0},value={1}", item.Key, item.Value);}Console.WriteLine("获取系统变量信息完毕。");}} }


using System; using System.Threading;namespace ConsoleApp1 {class Program{static void Main(string[] args){ThreadPoolDemo tpd1 = new ThreadPoolDemo();tpd1.Work();Thread.Sleep(5000);Console.WriteLine("OK");Console.ReadLine();}} }
利用ThreadPool调用工作线程和IO线程的范例


using System; using System.Collections; using System.IO; using System.Text; using System.Threading;namespace ConsoleApp1 {class Program{static void Main(string[] args){// 设置线程池中处于活动的线程的最大数目// 设置线程池中工作者线程数量为1000,I/O线程数量为1000ThreadPool.SetMaxThreads(1000, 1000);Console.WriteLine("Main Thread: queue an asynchronous method");PrintMessage("Main Thread Start");// 把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法 ThreadPool.QueueUserWorkItem(asyncMethod);asyncWriteFile();Console.Read();}// 方法必须匹配WaitCallback委托private static void asyncMethod(object state){Thread.Sleep(1000);PrintMessage("Asynchoronous Method");Console.WriteLine("Asynchoronous thread has worked ");}#region 异步读取文件模块private static void asyncReadFile(){byte[] byteData = new byte[1024];FileStream stream = new FileStream(@"D:\123.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 1024, true);//把FileStream对象,byte[]对象,长度等有关数据绑定到FileDate对象中,以附带属性方式送到回调函数Hashtable ht = new Hashtable();ht.Add("Length", (int)stream.Length);ht.Add("Stream", stream);ht.Add("ByteData", byteData);//启动异步读取,倒数第二个参数是指定回调函数,倒数第一个参数是传入回调函数中的参数stream.BeginRead(byteData, 0, (int)ht["Length"], new AsyncCallback(Completed), ht);PrintMessage("asyncReadFile Method");}//实际参数就是回调函数static void Completed(IAsyncResult result){Thread.Sleep(2000);PrintMessage("asyncReadFile Completed Method");//参数result实际上就是Hashtable对象,以FileStream.EndRead完成异步读取Hashtable ht = (Hashtable)result.AsyncState;FileStream stream = (FileStream)ht["Stream"];int length = stream.EndRead(result);stream.Close();string str = Encoding.UTF8.GetString(ht["ByteData"] as byte[]);Console.WriteLine(str);stream.Close();}#endregion#region 异步写入文件模块//异步写入模块private static void asyncWriteFile(){//文件名 文件创建方式 文件权限 文件进程共享 缓冲区大小为1024 是否启动异步I/O线程为trueFileStream stream = new FileStream(@"D:\123.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 1024, true);//这里要注意,如果写入的字符串很小,则.Net会使用辅助线程写,因为这样比较快byte[] bytes = Encoding.UTF8.GetBytes("你在他乡还好吗?");//异步写入开始,倒数第二个参数指定回调函数,最后一个参数将自身传到回调函数里,用于结束异步线程stream.BeginWrite(bytes, 0, (int)bytes.Length, new AsyncCallback(Callback), stream);PrintMessage("AsyncWriteFile Method");}static void Callback(IAsyncResult result){//显示线程池现状Thread.Sleep(2000);PrintMessage("AsyncWriteFile Callback Method");//通过result.AsyncState再强制转换为FileStream就能够获取FileStream对象,用于结束异步写入FileStream stream = (FileStream)result.AsyncState;stream.EndWrite(result);stream.Flush();stream.Close();asyncReadFile();}#endregion// 打印线程池信息private static void PrintMessage(String data){int workthreadnumber;int iothreadnumber;// 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量// 获得的可用I/O线程数量给iothreadnumber变量ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",data,Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsBackground.ToString(),workthreadnumber.ToString(),iothreadnumber.ToString());}} }
线程池中放入异步操作


using System; using System.Threading;namespace ConsoleApp1 {class Program{private static void AsyncOperation(object state){Console.WriteLine("Operation state: {0}", state ?? "(null)");Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(TimeSpan.FromSeconds(2));}static void Main(string[] args){const int x = 1;const int y = 2;const string lambdaState = "lambda state 2";ThreadPool.QueueUserWorkItem(AsyncOperation);Thread.Sleep(TimeSpan.FromSeconds(1));ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");Thread.Sleep(TimeSpan.FromSeconds(1));ThreadPool.QueueUserWorkItem(state => {Console.WriteLine("Operation state: {0}", state);Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(TimeSpan.FromSeconds(2));}, "lambda state");ThreadPool.QueueUserWorkItem(_ =>{Console.WriteLine("Operation state: {0}, {1}", x + y, lambdaState);Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(TimeSpan.FromSeconds(2));}, "lambda state");Thread.Sleep(TimeSpan.FromSeconds(2));}} }
线程池同步操作


using System; using System.Threading;namespace ConsoleApp1 {class ThreadPoolDemo{static object lockobj = new object();static int Count = 0;ManualResetEvent manualEvent;public ThreadPoolDemo(ManualResetEvent manualEvent){this.manualEvent = manualEvent;}public void DisplayNumber(object a){lock (lockobj){Count++;Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", a, Count, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);}//Console.WriteLine("当前运算结果:{0}", a);//Console.WriteLine("当前运算结果:{0},当前子线程id:{1} 的状态:{2}", a,Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);//这里是方法执行时间的模拟,如果注释该行代码,就能看出线程池的功能了Thread.Sleep(2000);//Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", a, Count, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);//这里是释放共享锁,让其他线程进入 manualEvent.Set();}} }


using System; using System.Diagnostics; using System.Threading;namespace ConsoleApp1 {class Program{//设定任务数量 static int count = 10;static void Main(string[] args){//让线程池执行5个任务所以也为每个任务加上这个对象保持同步ManualResetEvent[] events = new ManualResetEvent[count];Console.WriteLine("当前主线程id:{0}", Thread.CurrentThread.ManagedThreadId);Stopwatch sw = new Stopwatch();sw.Start();NoThreadPool(count);sw.Stop();Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);sw.Reset();sw.Start();//循环每个任务for (int i = 0; i < count; i++){//实例化同步工具events[i] = new ManualResetEvent(false);//Test在这里就是任务类,将同步工具的引用传入能保证共享区内每次只有一个线程进入ThreadPoolDemo tst = new ThreadPoolDemo(events[i]);//Thread.Sleep(200);//将任务放入线程池中,让线程池中的线程执行该任务 ThreadPool.QueueUserWorkItem(tst.DisplayNumber, i);}//注意这里,设定WaitAll是为了阻塞调用线程(主线程),让其余线程先执行完毕,//其中每个任务完成后调用其set()方法(收到信号),当所有//的任务都收到信号后,执行完毕,将控制权再次交回调用线程(这里的主线程) ManualResetEvent.WaitAll(events);sw.Stop();Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);//Console.WriteLine("所有任务做完!"); Console.ReadKey();}static void NoThreadPool(int count){for (int i = 0; i < count; i++){Thread.Sleep(2000);Console.WriteLine("当前运算结果:{0},Count={1},当前子线程id:{2} 的状态:{3}", i, i + 1, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);}}} }
线程池中的取消操作


using System; using System.Threading;namespace ConsoleApp1 {class Program{static void Main(string[] args){ThreadPool.SetMaxThreads(1000, 1000);Console.WriteLine("Main thread run");PrintMessage("Start");Run();Console.ReadKey();}private static void Run(){CancellationTokenSource cts = new CancellationTokenSource();// 这里用Lambda表达式的方式和使用委托的效果一样的,只是用了Lambda后可以少定义一个方法。// 这在这里就是让大家明白怎么lambda表达式如何由委托转变的////ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000)); ThreadPool.QueueUserWorkItem(callback, cts.Token);Console.WriteLine("Press Enter key to cancel the operation\n");Console.ReadLine();// 传达取消请求 cts.Cancel();Console.ReadLine();}private static void callback(object state){Thread.Sleep(1000);PrintMessage("Asynchoronous Method Start");CancellationToken token = (CancellationToken)state;Count(token, 1000);}// 执行的操作,当受到取消请求时停止数数private static void Count(CancellationToken token, int countto){for (int i = 0; i < countto; i++){if (token.IsCancellationRequested){Console.WriteLine("Count is canceled");break;}Console.WriteLine(i);Thread.Sleep(300);}Console.WriteLine("Cout has done");}// 打印线程池信息private static void PrintMessage(String data){int workthreadnumber;int iothreadnumber;// 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量// 获得的可用I/O线程数量给iothreadnumber变量ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",data,Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsBackground.ToString(),workthreadnumber.ToString(),iothreadnumber.ToString());}} }
Thread与ThreadPool的一个性能比较


using System; using System.Diagnostics; using System.Threading;namespace ConsoleApp1 {class Program{static void Main(string[] args){const int numberOfOperations = 300;var sw = new Stopwatch();sw.Start();UseThreads(numberOfOperations);sw.Stop();Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);sw.Reset();sw.Start();UseThreadPool(numberOfOperations);sw.Stop();Console.WriteLine("Execution time using threadPool: {0}", sw.ElapsedMilliseconds);}static void UseThreads(int numberOfOperations){using (var countdown = new CountdownEvent(numberOfOperations)){Console.WriteLine("Scheduling work by creating threads");for (int i = 0; i < numberOfOperations; i++){var thread = new Thread(() => {Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(TimeSpan.FromSeconds(0.1));countdown.Signal();});thread.Start();}countdown.Wait();Console.WriteLine();}}static void UseThreadPool(int numberOfOperations){using (var countdown = new CountdownEvent(numberOfOperations)){Console.WriteLine("Starting work on a threadpool");for (int i = 0; i < numberOfOperations; i++){ThreadPool.QueueUserWorkItem(_ => {Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(TimeSpan.FromSeconds(0.1));countdown.Signal();});}countdown.Wait();Console.WriteLine();}}} }