C#多线程之线程池ThreadPool用法
作者:.NET开发菜鸟 发布时间:2021-07-21 06:28:40
一、ThreadPool
ThreadPool是.Net Framework 2.0版本中出现的。
ThreadPool出现的背景:Thread功能繁多,而且对线程数量没有管控,对于线程的开辟和销毁要消耗大量的资源。每次new一个THread都要重新开辟内存。
如果某个线程的创建和销毁的代价比较高,同时这个对象还可以反复使用的,就需要一个池子(容器),保存多个这样的对象,需要用的时候从池子里面获取,用完之后不用销毁,在放到池子里面。这样不但能节省内存资源,提高性能,而且还能管控线程的总数量,防止滥用。这时就需要使用ThreadPool了。
我们来看看ThreadPool中常用的一些方法。
1、QueueUserWorkItem()
QueueUserWorkItem()方法用来启动一个多线程。我们先看看方法的定义:
QueueUserWorkItem()方法有一个WaitCallback类型的参数,在看看WaitCallback的定义:
可以看到WaitCallback就是有一个object类型参数的委托,所以ThreadPool启动多线程使用下面的代码:
using System;
using System.Threading;
namespace ThreadPoolDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
// ThreadPoll启动多线程
ThreadPool.QueueUserWorkItem(p => DoSomethingLong("启动多线程"));
Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Console.ReadKey();
}
static void DoSomethingLong(string para)
{
Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
}
}
运行结果:
2、GetMaxThreads()
GetMaxThreads()用来获取线程池中最多可以有多少个辅助线程和最多有多少个异步线程。
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
程序运行结果:
3、GetMinThreads()
GetMinThreads()用来获取线程池中最少可以有多少个辅助线程和最少有多少个异步线程。
ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads);
Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}");
程序运行结果:
4、SetMaxThreads()和SetMinThreads()
SetMaxThreads()和SetMinThreads()分别用来设置线程池中最多线程数和最少线程数。
using System;
using System.Threading;
namespace ThreadPoolDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
// ThreadPoll启动多线程
ThreadPool.QueueUserWorkItem(p => DoSomethingLong("启动多线程"));
// 获取最大线程
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
// 获取最小线程
ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads);
Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}");
// 设置线程池线程
SetThreadPool();
// 输出设置后的线程池线程个数
Console.WriteLine("输出修改后的最多线程数和最少线程数");
ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads);
Console.WriteLine($"GetMaxThreads workerThreads={maxworkerThreads} completionPortThreads={maxcompletionPortThreads}");
ThreadPool.GetMinThreads(out int workerEditThreads, out int completionPortEditThreads);
Console.WriteLine($"GetMinThreads workerThreads={workerEditThreads} completionPortThreads={completionPortEditThreads}");
Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Console.ReadKey();
}
static void DoSomethingLong(string para)
{
Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
/// <summary>
/// 设置线程池线程个数
/// </summary>
static void SetThreadPool()
{
Console.WriteLine("************设置最多线程数和最少线程数****************");
// 设置最大线程
ThreadPool.SetMaxThreads(16, 16);
// 设置最小线程
ThreadPool.SetMinThreads(8, 8);
}
}
}
程序运行结果:
二、线程等待
先来看下面一个小例子:
ThreadPool.QueueUserWorkItem(p => DoSomethingLong("启动多线程"));
Console.WriteLine("等着QueueUserWorkItem完成后才执行");
我们想让异步多线程执行完以后再输出“等着QueueUserWorkItem完成后才执行” 这句话,上面的代码运行效果如下:
从截图中可以看出,效果并不是我们想要的,Thread中提供了暂停、恢复等API,但是ThreadPool中没有这些API,在ThreadPool中要实现线程等待,需要使用到ManualResetEvent类。
ManualResetEvent类的定义如下:
ManualResetEvent需要一个bool类型的参数来表示暂停和停止。上面的代码修改如下:
// 参数设置为false
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(p =>
{
DoSomethingLong("启动多线程");
// 设置为true
manualResetEvent.Set();
});
//
manualResetEvent.WaitOne();
Console.WriteLine("等着QueueUserWorkItem完成后才执行");
结果:
ManualResetEvent类的参数值执行顺序如下:
(1)、false--WaitOne等待--Set--true--WaitOne直接过去
(2)、true--WaitOne直接过去--ReSet--false--WaitOne等待
注意:一般情况下,不要阻塞线程池中的线程,因为这样会导致一些无法预见的错误。来看下面的一个例子:
static void SetWait()
{
// 设置最大线程
ThreadPool.SetMaxThreads(16, 16);
// 设置最小线程
ThreadPool.SetMinThreads(8, 8);
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
for (int i = 0; i < 20; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem(p =>
{
Console.WriteLine(k);
if (k < 18)
{
manualResetEvent.WaitOne();
}
else
{
// 设为true
manualResetEvent.Set();
}
});
}
if (manualResetEvent.WaitOne())
{
Console.WriteLine("没有死锁、、、");
}
else
{
Console.WriteLine("发生死锁、、、");
}
}
启动20个线程,如果k小于18就阻塞当前的线程,结果:
从截图中看出,只执行了16个线程,后面的线程没有执行,这是为什么呢?因为我们在上面设置了线程池中最多可以有16个线程,当16个线程都阻塞的时候,会造成死锁,所以后面的线程不会再执行了。
三、线程重用
ThreadPool可以很好的实现线程的重用,这样就可以减少内存的消耗,看下面的代码:
/// <summary>
/// 测试ThreadPool线程重用
/// </summary>
static void ThreadPoolTest()
{
// 线程重用
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
Thread.Sleep(10 * 1000);
Console.WriteLine("前面的计算都完成了。。。。。。。。");
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool"));
}
然后在Main方法里面调用该方法,输出结果如下图所示:
我们在代码里面总共创建了10个线程,而结果里面只有4个线程ID,这就说明ThreadPool可以实现线程的重用。下面我们在看看Thread是否可以实现线程的重用,代码如下:
/// <summary>
/// 测试Thread线程重用
/// </summary>
static void ThreadTest()
{
for (int i = 0; i < 5; i++)
{
new Thread(() => DoSomethingLong("Threads")).Start();
}
Thread.Sleep(10 * 1000);
Console.WriteLine("前面的计算都完成了。。。。。。。。");
for (int i = 0; i < 5; i++)
{
new Thread(() => DoSomethingLong("btnThreads")).Start();
}
}
然后在Main方法里面调用,输入结果如下图所示:
我们同样在代码里面创建了10个线程,结果输出了10个线程的ID,这就说明Thread不能实现线程的重用。同样也说明THread的效率没有ThreadPool高。
程序完整代码:
using System;
using System.Threading;
namespace ThreadPoolDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//// ThreadPoll启动多线程
//ThreadPool.QueueUserWorkItem(p => DoSomethingLong("启动多线程"));
//// 获取最大线程
//ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
//Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
//// 获取最小线程
//ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads);
//Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}");
//// 设置线程池线程
//SetThreadPool();
//// 输出设置后的线程池线程个数
//Console.WriteLine("输出修改后的最多线程数和最少线程数");
//ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads);
//Console.WriteLine($"GetMaxThreads workerThreads={maxworkerThreads} completionPortThreads={maxcompletionPortThreads}");
//ThreadPool.GetMinThreads(out int workerEditThreads, out int completionPortEditThreads);
//Console.WriteLine($"GetMinThreads workerThreads={workerEditThreads} completionPortThreads={completionPortEditThreads}");
//Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//// 参数设置为false
//ManualResetEvent manualResetEvent = new ManualResetEvent(false);
//ThreadPool.QueueUserWorkItem(p =>
//{
// DoSomethingLong("启动多线程");
// // 设置为true
// manualResetEvent.Set();
//});
////
//manualResetEvent.WaitOne();
//Console.WriteLine("等着QueueUserWorkItem完成后才执行");
// SetWait();
// ThreadPool实现线程的重用
// ThreadPoolTest();
// Thread
ThreadTest();
Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Console.ReadKey();
}
static void DoSomethingLong(string para)
{
Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
/// <summary>
/// 设置线程池线程个数
/// </summary>
static void SetThreadPool()
{
Console.WriteLine("************设置最多线程数和最少线程数****************");
// 设置最大线程
ThreadPool.SetMaxThreads(16, 16);
// 设置最小线程
ThreadPool.SetMinThreads(8, 8);
}
static void SetWait()
{
// 设置最大线程
ThreadPool.SetMaxThreads(16, 16);
// 设置最小线程
ThreadPool.SetMinThreads(8, 8);
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
for (int i = 0; i < 20; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem(p =>
{
Console.WriteLine(k);
if (k < 18)
{
manualResetEvent.WaitOne();
}
else
{
// 设为true
manualResetEvent.Set();
}
});
}
if (manualResetEvent.WaitOne())
{
Console.WriteLine("没有死锁、、、");
}
else
{
Console.WriteLine("发生死锁、、、");
}
}
/// <summary>
/// 测试ThreadPool线程重用
/// </summary>
static void ThreadPoolTest()
{
// 线程重用
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
Thread.Sleep(10 * 1000);
Console.WriteLine("前面的计算都完成了。。。。。。。。");
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool"));
}
/// <summary>
/// 测试Thread线程重用
/// </summary>
static void ThreadTest()
{
for (int i = 0; i < 5; i++)
{
new Thread(() => DoSomethingLong("Threads")).Start();
}
Thread.Sleep(10 * 1000);
Console.WriteLine("前面的计算都完成了。。。。。。。。");
for (int i = 0; i < 5; i++)
{
new Thread(() => DoSomethingLong("btnThreads")).Start();
}
}
}
}
来源:https://www.cnblogs.com/dotnet261010/p/9124380.html
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- ThreadLocal类,代表一个线程局部变量,通过把数据放在ThreadLocal中,可以让每个线程创建一个该变量的副本。也可以看成是线程
- 今天看到一个ios写的图灵机器人,直接去官网(http://www.tuling123.com/openapi/)看了下API接入,太简单了
- 本文实例讲述了Java上传文件进度条的实现方法。分享给大家供大家参考,具体如下:东西很简单,主要用到commons-fileupload,其
- 在说ClassCastException之前,先介绍下引用类型转换;引用类型转换分为向上转型和向下转型两种; 向上转型:多态本身是
- 理论基础so的加载是一种解析式装载,这与dex有一定区别,dex是先加载进行优化验证生成odex,再去解析odex文件,而so更像边解析边装
- 1.spring配置文件<bean id="configproperties"  
- 本文实例为大家分享了使用C#写一个时钟,供大家参考,具体内容如下时钟是这样的一共使用四个控件即可:WinFrom窗体应用程序代码:using
- log4j配置失效日志中打印Debug信息最近发布项目的时候发现控制台打印的日志较往常多了很多,仔细一看,debug和info信息也赫然在列
- 本文将介绍通过Java程序来读取PDF文档中的文本和图片的方法。分别调用方法extractText()和extractImages()来读取
- 本文实例讲述了Java面向对象程序设计:继承,多态用法。分享给大家供大家参考,具体如下:本文内容:继承多态首发时期:2018-03-23继承
- 本文实例讲述了C++实现的链表类。分享给大家供大家参考。具体如下:#include <iostream>using namesp
- Idea中directory和package的区别,要是错了就右键,make directory as 目录或者源代码目录(Source R
- 背景java程序员一般写的是后端服务是JavaWeb类型的项目,主要包括Http接口和dubbo接口,Http接口一般采用的rest风格,那
- 将IDEA的默认快捷键设置 设置成为 Eclipse的默认快捷键设置题意有些拗口,但没关系,就是将idea中的快捷键转为自己刚学Java时使
- Java计算一段程序的运行时间介绍了两种方法,一种是毫秒级别的计算,另一种是更精确的纳秒级别的计算。毫秒级别计算时间  
- 需求:request的content-type为applciation/json,进入controller之前需要把body中的参数取出来做
- 1、前言Android Studio对模块化开发提供的一个很有用的功能就是可以在主项目下新建库项目(Module),但是在使用库项目时却有一
- mybatis Integer类型参数动态sql注意条件例如以下拼接的动态sql<if test="work_status
- 前文传送门:Netty分布式高性能工具类同线程下回收对象解析异线程回收对象就是创建对象和回收对象不在同一条线程的情况下, 对象回收的逻辑我们
- 使用Apache.POI中HSSFWorkbook导出到Excel,具体内容如下所示:1.引入Poi依赖(3.12)依赖如下:<dep