一、线程池的概念
1、线程的创建和销毁要付出较高的代价,频繁创建和销毁线程会降低系统的性能。线程池就是一些在应用程序起始时就创建好的线程,这些线程可用于执行应用程序需要的不同操作。
2、线程池为每个任务提供一个工作线程,任务执行完后工作线程的状态会重置,以便执行下一个任务,而不是被销毁。
3、线程池有一个任务队列,所有等待执行的任务都进入任务队列,在有空闲线程时会从队列中取出一个任务执行,如果没有空闲线程就等待直到有线程可用。
using System; using System.Threading; namespace ThreadPoolExample { class Program { static void Main(string[] args) { for (int i = 0; i < 10; i++) { ThreadPool.QueueUserWorkItem(PrintHello); } Console.ReadKey(); } private static void PrintHello(object state) { Console.WriteLine($"Hello from thread {Thread.CurrentThread.ManagedThreadId}"); } } }
二、线程池的使用
1、线程池的创建
线程池是由framework提供的,可以使用ThreadPool类的静态方法来创建。ThreadPool.GetMaxThreads()方法返回可用于线程池的最大线程数。ThreadPool.GetAvailableThreads()方法返回线程池中空闲线程的数量。
int maxThreads; int availableThreads; ThreadPool.GetMaxThreads(out maxThreads, out availableThreads); Console.WriteLine($"Max threads: {maxThreads}. Available threads: {availableThreads}");
2、线程池的任务添加
可以使用ThreadPool.QueueUserWorkItem()方法添加任务到线程池,该方法接受一个委托作为参数,需要执行的代码可以是匿名方法或普通方法。
ThreadPool.QueueUserWorkItem(new WaitCallback(PrintHello)); ThreadPool.QueueUserWorkItem(PrintHello);
3、线程池的工作队列
可以使用ThreadPool.GetQueuedThreadInfo()方法获取线程池正在排队的工作线程的一些信息。
ThreadPool.GetQueuedThreadInfo(out int queueLength, out int workerThreads); Console.WriteLine($"Queue length: {queueLength}. Worker threads: {workerThreads}");
三、线程池的优化
1、线程池的自定义大小
线程池的大小默认是由CLR制定的,但可以通过调用ThreadPool.SetMaxThreads()和ThreadPool.SetMinThreads()方法来自定义。
ThreadPool.SetMaxThreads(4, 4); ThreadPool.SetMinThreads(2, 2);
2、使用线程池执行长时间任务
由于线程池的默认大小是有限的,如果存在一个耗时很长的任务,会占用线程池的大部分线程,导致其他任务等待执行。
可以将长时间任务分成多个小任务,并使用异步方式执行,以便在等待长时间任务完成时释放线程池中的线程。
for (int i = 0; i < 100; i++) { ThreadPool.QueueUserWorkItem(state => { // DoSomeWork()是一个耗时很长的方法 var result = DoSomeWork(); // 执行完小任务之后,释放线程池中的线程 }); }
3、避免线程池阻塞
线程池中的每个线程默认都是前台线程,会保持程序运行。当调用某些方法时,如Thread.Sleep()或 Thread.Join(),会产生阻塞线程池的效果。
为了避免线程池被阻塞,可以创建一个后台线程,代替前台线程,使用ManualResetEvent.WaitOne()方法等待。
static void Main(string[] args) { var resetEvent = new ManualResetEvent(false); var thread = new Thread(() => { // DoSomethingLong(); resetEvent.Set(); }) { IsBackground = true }; thread.Start(); resetEvent.WaitOne(); }
四、线程池使用场景
线程池适用于需要执行大量可以异步执行的任务的情况。常见的应用包括:网络套接字通信的异步执行、大量文件I / O异步执行、在多个客户端中异步执行长时间操作以提高Web服务器吞吐量等。
五、参考链接
c#线程池:https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=net-5.0
c#线程池介绍:https://www.cnblogs.com/hanyinglong/p/6913031.html